Skip to content

How to change WordPress permalinks without breaking your old links

WordPress keeps no record of a permalink structure you leave. After a change, some old post addresses are forwarded by a guess and the rest answer 404. A move from a dated structure to Post name answers 404. Add one 301 rule for the old pattern, then check an old address.

By
WP Ministry
Published
Tested on
WordPress 7.1.3, PHP 8.3.35

In short

  • The structure is set under Settings, then Permalinks. It builds the addresses of posts. Pages keep theirs.
  • WordPress keeps no list of old structures. A plain address is always forwarded. Any other is forwarded only if WordPress can guess the post from it.
  • After a move from Day and name or Month and name to Post name, every old post address answers 404.
  • WordPress forwards a post whose slug changed. It does not do that for a page.
  • One 301 rule in .htaccess forwards a whole dated structure. Remove it before you ever change back.
  • Google's guidance is permanent redirects, kept for at least a year.

Changing the permalink structure changes the address of every post at once. WordPress saves the new structure and keeps nothing of the old one. Whether an old address still leads anywhere depends on the structure you left and the one you chose. After a change from a dated structure to Post name, every old post address answers 404.

So the safe order is this: save a list of the addresses you have now, change the structure, ask for an old address and read the answer, and add one 301 redirect rule if WordPress does not forward it. If you have already made the change and old links answer 404, the table below shows why, and the rule in the steps is the cure.

A permalink is the full address of one post, page or archive. The permalink structure is the pattern WordPress builds post addresses from. It is one setting, written with tags such as %year% and %postname%.

In the dashboard it is under Settings, then Permalinks. The screen is titled "Permalink Settings". Under "Common Settings", the row "Permalink structure" offers "Plain", "Day and name", "Month and name", "Numeric", "Post name" and "Custom Structure". Under "Optional" are two more fields, "Category base" and "Tag base". Nothing changes until you press "Save Changes".

StructureExample addressWhen it suits
Plainhttps://example.com/?p=123A server that cannot rewrite addresses. WordPress's documentation says this one works on every server. The address says nothing about the post.
Day and namehttps://example.com/2026/10/08/sample-post/Writing where the day matters to the reader, such as news. Change a post's date and its address changes with it.
Month and namehttps://example.com/2026/10/sample-post/The same, with a shorter address.
Numerichttps://example.com/archives/123An address that never changes with the title or the date. It holds a number and no words.
Post namehttps://example.com/sample-post/Content that is not tied to a date. The address is the post's slug and nothing else.
Custom Structurehttps://example.com/blog/sample-post/, from /blog/%postname%/Keeping posts under a word of their own, apart from pages. The word is added to category archives too.

Google's advice on addresses is general: use readable words where possible, not long ID numbers. Every structure except Plain and Numeric puts the post's name in the address.

The structure is for posts. A page's address is built from the page's own slug. "Pages and category archives" below says when it moves.

From the command line, WP-CLI prints the structure a site has now. An empty line means Plain.

bash
wp option get permalink_structure

What a new site starts with

WordPress's documentation gives two answers. Its page on customizing permalinks says the default is Plain. Its page on the Permalinks screen says WordPress by default uses addresses with the day and name in them. Each is right about some sites.

During installation WordPress switches on Day and name, asks for its own first post at the new address, and keeps the structure only if WordPress answers there. If not, it tries the same structure with /index.php in front, and failing that it goes back to Plain. So a site installed where the server rewrites addresses and can reach itself starts on Day and name. A site installed where that request fails starts on Plain. Look at your own setting, and do not assume either.

What an old address answers after a change

WordPress keeps the structure the site has now and nothing about the one before. There is no list of former addresses. Two things can still forward an old address, and both belong to what WordPress calls its canonical redirect.

  • A plain address is turned into the permalink. /?p=123 is sent to the post's current address with a 301. So are a page's ?page_id= address and a category's ?cat= address. A move away from Plain is forwarded in full.
  • A guess. When a request ends in "not found" and WordPress's current rules read a post name out of the address, WordPress looks for a published post whose slug begins with that name. If it finds one, it sends the visitor there with a 301.

The guess needs a name. Whether the rules of the new structure can read one out of an address written in the old structure depends on the shape of both, which is why the answer differs by direction. This is what the old address of a post answers after each change between the five named structures.

The old address wasNow Day and nameNow Month and nameNow Post nameNow Numeric
Plain, /?p=123301301301301
Day and name, /2024/06/15/summer-sale/301404301
Month and name, /2024/06/summer-sale/301404301
Post name, /summer-sale/301301301
Numeric, /archives/123404404404

Each 301 leads straight to the post's new address. Four things follow from the table.

  • A move from a dated structure to Post name is not forwarded. Under Post name, WordPress has no rule for an address with a date in front of the name. It reads nothing from /2024/06/15/summer-sale/, makes no guess, and answers 404. Every old post address stays that way until you add a redirect.
  • A move away from Numeric is not forwarded. The old address holds a number and no name.
  • A custom structure with a word in front behaves the same way. After a change from Post name to /blog/%postname%/, the old /summer-sale/ answers 404. After the change back, /blog/summer-sale/ is forwarded.
  • A move to Plain forwards nothing. Saved on the Permalinks screen, the change also empties WordPress's block in .htaccess when WordPress may write to that file. Every old address, pages and category archives included, then gets the server's own "Not Found" page. Where the block stays in the file, as it does after a change made with WP-CLI, an old address answers 200 and shows the home page in place of the post.

The guess is not a map. It matches the beginning of a slug, so /summer/ is forwarded to a post whose slug is summer-sale, and it takes one match where several posts begin alike. A plugin or a theme can also switch it off. Read the table as what WordPress does when left alone, then ask your own site.

Pages and category archives

A page keeps its address through every change between Day and name, Month and name, Post name and a custom structure such as /blog/%postname%/. Two cases differ. Under Plain a page is at /?page_id=123. And Numeric ends without a slash, so under it /our-team/ is redirected to /our-team.

A category archive keeps its address, /category/news/, under Day and name, Month and name and Post name. It moves when the structure begins with a fixed word: to /archives/category/news under Numeric, and to /blog/category/news/ under /blog/%postname%/. The address it left then answers 404. The same happens when you change "Category base": with the base set to topics, the archive is at /topics/news/ and /category/news/ answers 404. WordPress forwards none of these.

What WordPress forwards when one slug changes

Changing one post's slug is a different case, and here WordPress does keep a record. When the slug of a published post changes, WordPress saves the old slug with the post. A later request for the old address finds no post, WordPress looks the old slug up, and answers 301 with the post's current address.

It keeps the same kind of record when a published post's date changes. That matters under a dated structure: the address with the old date is forwarded to the address with the new one.

This covers posts. It does not cover pages. The function that saves the old slug skips every kind of content that has parents and children, and pages are one. After a page's slug changes, its old address answers 404 until you add a redirect yourself. How to set up redirects in WordPress has the three places one can live.

A redirect made by WordPress carries a header that names it. A redirect made by a rule in your server's configuration does not, so the header shows which of the two answered.

text
HTTP/1.1 301 Moved Permanently
X-Redirect-By: WordPress
Location: https://example.com/june-offer/
  1. Step 1: Save a list of the addresses you have now

    After the change WordPress can no longer tell you what the old addresses were. This prints the address of every published post and saves the list to a file in your home folder.

    Without WP-CLI, save the site's sitemap. The sitemap WordPress makes at /wp-sitemap.xml says where to find it.

    bash
    wp post list --post_type=post --post_status=publish --field=url | tee ~/old-addresses.txt
  2. Step 2: Find your change in the table above

    If your change shows 301, WordPress forwards the old post addresses. Check a few all the same. If it shows 404, you need the rule in the fifth step.

  3. Step 3: Change the structure

    In the dashboard, choose the new structure under Settings, then Permalinks, and press "Save Changes". With WP-CLI, give the structure in single quotes. This one is Post name.

    It answers "Success: Rewrite structure set." and "Success: Rewrite rules flushed." Load a post at its new address. If the new address answers 404, stop and go to "If the new addresses answer 404" below.

    bash
    wp rewrite structure '/%postname%/'
  4. Step 4: Ask for an old address and read the answer

    Take an address from your list. curl -sI asks for the headers only and does not follow a redirect.

    The first line is the status. HTTP/1.1 404 Not Found means nothing forwards this address. A 301 with a Location line means it is forwarded, and the line says where to. The redirect checker shows the same without a terminal.

    bash
    curl -sI https://example.com/2024/06/15/summer-sale/
  5. Step 5: Add a redirect for the old pattern

    This rule is for a site that has moved from Day and name to Post name on Apache. Put it at the top of .htaccess, above the line # BEGIN WordPress. WordPress writes the lines between its two markers itself, and overwrites any change made there.

    The pattern matches four digits, two digits, two digits and one more part, with or without a final slash. $1 puts that last part into the new address.

    .htaccess
    RedirectMatch 301 "^/[0-9]{4}/[0-9]{2}/[0-9]{2}/([^/]+)/?$" "/$1/"
  6. Step 6: Ask again, and follow it to the end

    Run the same request. Among the lines of the answer should be these two, with your own domain. Then ask for the new address, which should answer 200 with no further redirect.

    text
    HTTP/1.1 301 Moved Permanently
    Location: https://example.com/summer-sale/
  7. Step 7: Update what still points at the old addresses

    Links that WordPress builds change by themselves, the sitemap among them: it lists the new addresses as soon as the structure is saved. Links typed into the text of posts and pages are not touched. They keep the old address and reach the post through the redirect. Google's guide to moving a site says to change a site's own links to the new addresses. Edit the ones that matter most first, and leave the redirect in place for the rest.

What the rule covers and what it does not

  • The target has no domain in it. Apache adds the scheme and host name of its own server. If the site sits behind a proxy or a CDN that talks to visitors over https and to your server over http, write the target in full, as "https://example.com/$1/" with your own domain, so the redirect does not pass through an http address.
  • Date archives are left alone. /2024/06/15/ has nothing after the day, so it does not match. It is still a valid address under Post name.
  • A post's own sub-addresses are not matched. The comment feed at /2024/06/15/summer-sale/feed/ has a fifth part and answers 404.
  • One address that is not a post does match. The feed of a single day's archive, /2024/06/15/feed/, is sent to /feed/.
  • It does not check that the post exists. A dated address for a post that was never there is sent on too, and answers 404 at its new address.

For Month and name the pattern has one date part fewer, and it must not catch the day archives, which have the same shape as an old post address. For a list of single addresses, a folder, or nginx, How to set up redirects in WordPress has each form. The redirect map builder turns a list of old and new addresses into rules.

To undo the redirect, remove the line. Remove it before you ever set the dated structure again. With the rule still in place, the rule sends the dated address to the short one and WordPress sends the short one back, and the browser gives up on the loop.

If the new addresses answer 404

Right after a change there are two kinds of 404, and they have different cures. Old addresses failing is the subject of everything above. New addresses failing means the structure was saved and the rules that carry it were not written. WordPress keeps those rules in two places.

WordPress's own list of addresses

The Permalinks screen and wp rewrite structure rebuild this list when they save. A change that reaches the setting some other way does not rebuild it, such as a script or a tool that updates the option directly. The site then answers by the old list. New addresses get a "not found" page in the site's own design, and the old ones still load. Rebuild the list:

bash
wp rewrite flush

WordPress's documentation says that opening the Permalinks screen does the same.

The WordPress block in .htaccess

On Apache, a few lines between # BEGIN WordPress and # END WordPress hand every address to WordPress. Without them the home page loads and every other address gets the server's own page, which says "The requested URL was not found on this server."

The block is the same for every structure except Plain, under which WordPress leaves it empty. So it matters on the first move away from Plain, or after a move to another server, and not on a change between two structures on a site that already works.

When you save the Permalinks screen, WordPress writes the block itself if it may write to the file, and answers "Permalink structure updated." If the file lacks the block and WordPress may not write to it, the structure is saved all the same. The screen then says "You should update your .htaccess file now." and shows the rules to paste in. Until they are in the file, the new addresses answer 404. How to fix 404 errors on posts and pages that exist has the block, and the nginx line that does the same job.

WP-CLI leaves .htaccess alone unless it is set up to write it. wp rewrite structure and wp rewrite flush change WordPress's list only. With --hard they write the block too, but only when WP-CLI's own configuration says Apache has mod_rewrite. Without that, WP-CLI prints "Warning: Regenerating a .htaccess file requires special configuration. See usage docs." and the file stays as it was. The configuration is two lines in a file named wp-cli.yml, in the folder you run WP-CLI from.

wp-cli.yml
apache_modules:
  - mod_rewrite
bash
wp rewrite flush --hard

Then look at the file, not at the message. WP-CLI answers "Success: Rewrite rules flushed." even when .htaccess is read-only and nothing was written to it.

What Google says about changed addresses

Google's documentation counts a change of paths on the same domain as a site move. Its guide to moving a site says this:

  • Use permanent redirects made by the server, such as 301 and 308. It says they cause no loss of PageRank.
  • Redirect each address straight to its final address. Googlebot can follow a chain of up to 10 redirects, and Google's advice is not to make one.
  • Do not send many old addresses to one unrelated page, such as the home page. Google may treat that as a soft 404.
  • Leave them in place. In its words: "Keep the redirects for as long as possible, generally at least 1 year."
  • Expect it to take time. As a general rule a small or medium site can take a few weeks for most pages to move, and how the site shows in Search may go up and down for a while during the move.

The Change of Address tool in Search Console is not for this. Google says it is only for a move to another domain or subdomain.

None of that is a promise about where a page will appear afterward, and nobody can make one. What you can check is that every old address answers 301, in one hop, to the right page. How to migrate a website without losing SEO covers a move in full. Changing the site's own address, its domain or http to https, is a different job: see how to change your WordPress URL.

When to get help

  • Ask the host if the site runs on nginx, or .htaccess cannot be edited. The rule then belongs in the server's own configuration.
  • Hand it over if old addresses still answer 404 with the rule in place, or the site has several old structures layered on one another. A one-time fix from WP Ministry covers one issue on one site and starts with a free diagnosis, which gives you a written cause and a fixed quote.

Common questions

Do I need redirects when I move from Plain to Post name?

No. WordPress forwards a plain address such as /?p=123 to the post's permalink with a 301, and does the same for pages and categories. What that move does need, on Apache, is the WordPress block in .htaccess.

Will changing the structure change the addresses of my pages?

Not between Day and name, Month and name, Post name and a custom structure such as /blog/%postname%/. Under Plain a page is at /?page_id=123, and under Numeric a page's address loses its final slash. Category archives move when the structure begins with a fixed word.

Can I change back if it goes wrong?

Yes. Set the old structure again and the old addresses answer at once. Take your redirect rule out of .htaccess first, or the two will send each address back and forth.

How long do the redirects have to stay?

Google's guidance for a site move is to keep them for as long as possible, and generally for at least a year. It adds that, for visitors' sake, you should consider keeping them indefinitely.

Should I change the structure of a site that already has visitors?

Only with a reason. WordPress's documentation says a permalink is meant to be permanent and never to change. Google's says that with any significant change to a site, how it shows in Search may move about while the site is crawled again.

My site runs on nginx. Where does the rule go?

nginx has no .htaccess. The redirect goes in the site's server block and takes effect when nginx is reloaded. How to set up redirects in WordPress shows the nginx form of a pattern redirect.

More on this subject

Quick Fix, done for you

Quick Fix is $49. One issue, one site, up to about an hour. No fix, no fee. 30-day warranty. It starts with a free diagnosis.