Removing the date permalink structure in WordPress

I was helping a client update their WordPress blog to remove the date directories (ex: /2023/01/01/) from the URL scheme and thought I’d include a few notes here on how we approached it.

Before you read through, I’ll just note that we did all of these steps on a staging environment first, testing along the way, before repeating them on their production environment.

Update Permalinks

The first step was pretty simple- just update the Permalinks in the Settings to the new structure:

Once you’ve done that, most of WordPress will be taken care of: links to posts, canonical links, links inside of archive pages and query loops, sitemaps, etc. Updating the permalink structure updates all of that, just be sure to clear any cache.

Update internal links

Updating the permalinks handles any URLs dynamically generated by WordPress, but when it comes to internal links (links from one post to another post inside of the post content), you’ll need to update those manually.

Note: We will set up a redirect in the next step, but for links inside your content- especially internal links- it’s a best practice to link directly to a destination, not to a redirect.

For this project, we chose to use WP-CLI to execute a regular expression search-and-place for us. Here’s the full command, but let’s break it down after:

wp search-replace '/(example\.com)\/\d{4}\/\d{2}\/\d{2}/' '' wp_posts --regex --include-columns=post_content,guidCode language: Bash (bash)

wp search-replace – Initializes the search-replace command, which needs at least two options, what to search for and what to replace it with.

'/(example\.com)\/\d{4}\/\d{2}\/\d{2}/' – Here we’re defining a regular expression for capturing URLs that specifically begin with our domain example.com and then are followed by the exact pattern of /%year%/%monthnum%/%day%/.

'' – What do we replace it with? Nothing. We simply want to strip the dates out of the URL. What about our domain, example.com? Because of the way we structured the capturing group, the domain will not be part of the actual match that gets replaced.

wp_posts – We’re limiting our search to the wp_posts table, rather than the entire database. This definitely depends on your specific site. For example, you may want to search the wp_postmeta table or you might have an e-commerce plugin with custom tables. Just note that some URLs may be stored as serialized data so you can’t be sure that every instance of the URL will be updated. Depending on the site’s complexity, you may need more than one command to catch everything.

--include-columns=post_content,guid – We’re limiting our search specifically to the post_content and guid columns. There’s no reason to spend extra time on any of the other columns.

--regex - Our search is for a regular expression, not just a string.

Not included here- I also recommend running this command with the --dry-run flag before running it as is. And of course – make sure you have a backup of your database handy.

And finally, be sure to clear that cache again.

Add a redirect for external links

Now that we’ve updated our internal links, we still want to set up a global redirect to catch any external links to our site that have the older URLs. There’s a number of ways to handle this, but I’ll just note the simplest way- using the Redirection plugin. They have a setting in the Site Options where you can note your old permalink structure, and they’ll set up a global redirect for you automatically.

And that’s it! To recap:

  • Set the new Permalink structure in the WordPress settings page.
  • Use WP-CLI to run a search-replace on any database tables/columns that will have internal links.
  • Set up a global redirect using Redirection (or directly in your server’s config or .htaccess file).
  • Clear that cache!

Did I miss anything? Let me know in the comments.

Learn Modern WordPress Development

I’m sharing the best resources and tutorials for developers building in and on the block editor.


Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.