Migrating WordPress Permalinks Without Losing Rankings
Problem Statement
You need to change a WordPress site’s permalink structure — for example from /?p=123 or /2019/03/post-name/ to /category/post-name/ — without dropping rankings or stranding inbound links. WordPress will silently 404 every old URL the moment you save the new structure unless you generate a deterministic 301 map and serve it from a layer that fires before WordPress renders. This page is part of the broader CMS & Framework Routing Changes section.
When to Use This Approach
- You are changing the permalink structure on a live, indexed WordPress site.
- Old URLs have backlinks, bookmarks, or organic traffic you cannot afford to lose.
- You want the old-to-new mapping in version control, not buried in plugin tables.
- You are deciding between the Redirection plugin (application layer) and server rules (
.htaccess/Nginx). - You are moving WordPress behind a new domain or onto a different host at the same time.
Step-by-Step Instructions
1. Export the Current URL List Before Changing Anything
Capture every published URL under the old structure so you can map it deterministically. WP-CLI gives you a clean export without touching the database.
# Export post ID, status, and current permalink for all published posts and pages
wp post list --post_type=post,page --post_status=publish \
--fields=ID,post_title,url --format=csv > old-urls.csv
2. Change the Permalink Structure and Flush Rules
Set the new structure once, then flush rewrite rules so WordPress writes the new internal routing. The --hard flag updates .htaccess on Apache.
# Apply new permalink structure and regenerate rewrite rules
wp rewrite structure '/%category%/%postname%/' --hard
wp rewrite flush --hard
3. Generate the 301 Map
Build an explicit old→new mapping. Because post IDs are stable, you can resolve each old URL to its new permalink and emit redirect lines.
# Emit "old_path new_path" pairs for each published post (IDs are stable across the change)
wp post list --post_type=post --post_status=publish --field=ID | \
while read id; do
printf '%s %s\n' "/?p=$id" "$(wp post url $id)"
done > redirect-map.txt
4. Deploy the Map at the Server Layer
Server rules fire before PHP loads, so they are faster and survive plugin failures. On Apache, place rules in .htaccess above the WordPress block; on Nginx, add a map and a redirect.
# .htaccess — must sit ABOVE the "# BEGIN WordPress" block so it wins
Redirect 301 /2019/03/old-post-name/ /technical/old-post-name/
RedirectMatch 301 ^/\?p=123$ /technical/old-post-name/
# Nginx — map old paths to new, then redirect on a hit
map $request_uri $wp_redirect {
/2019/03/old-post-name/ /technical/old-post-name/;
}
server {
if ($wp_redirect) { return 301 $wp_redirect; }
}
5. Choose Plugin Rules Only Where Server Rules Cannot Reach
If you lack server access (managed WordPress hosts often block .htaccess edits), import the same map into the Redirection plugin. It works but adds a PHP round-trip per redirect, so reserve it for hosts where server rules are impossible.
# Export the Redirection plugin's rules so the map stays in version control
wp redirection export 1 json > redirection-rules.json
Worked Example
Before: https://www.example.com/2019/03/migrating-dns/ returns HTTP/1.1 200. After changing the structure to /%category%/%postname%/, that URL would 404. With the .htaccess rule Redirect 301 /2019/03/migrating-dns/ /dns/migrating-dns/ in place:
$ curl -sIL https://www.example.com/2019/03/migrating-dns/
HTTP/1.1 301 Moved Permanently
Location: https://www.example.com/dns/migrating-dns/
HTTP/1.1 200 OK
The old dated URL now resolves in a single hop to the new category-based permalink, preserving the inbound link equity.
Verification
- Confirm a single-hop 301 on a sample of old URLs:
curl -sIL https://www.example.com/2019/03/migrating-dns/ | grep -iE '^HTTP|^location'. - Re-crawl the old URL list (
old-urls.csv) and confirm zero 404s and zero chains longer than one hop. - In Google Search Console, watch the legacy URL pattern move from Indexed to Redirect over the following crawl cycles.
FAQ
Should I use the Redirection plugin or server rules?
Use server rules (.htaccess or Nginx) when you have server access — they fire before PHP loads, so they are faster and keep working even if WordPress is down. Use the Redirection plugin only on managed hosts that block server-level redirect edits.
Will changing permalinks lose my rankings? Not if every old URL 301s to its exact new equivalent in a single hop. Rankings drop when old URLs 404, redirect to the homepage, or pass through a chain — generate a deterministic map and verify it before you announce the change.
Related
← Back to CMS & Framework Routing Changes