Opens in a new tab

Why does WordPress automatically redirect URLs when permalinks are enabled?

Symptom

We encountered this issue on a client's website: A URL like /gift-certificates/ landed at 301 /xxx/gift-certificates-for-singing-workshop/.

  1. No redirect plugin was active,
  2. in the .htaccess And there were no redirects in the nginx configuration—nothing.
  3. All plugins were disabled
  4. The redirection persisted.

If you set the permalinks to „Simple“—that is, in the format example.test/?p=123 instead of example.test/123/—this issue does not occur.

The HTTP header clearly points to WordPress as the source:

curl -skI https://example.test/coupons/Code language: JavaScript (javascript)

provides:

HTTP/2 301
location: https://example.test/singen-lernen-shop/gutscheine-fuer-gesangs-workshop/
x-redirect-by: WordPressCode language: HTTP (http)

x-redirect-by: WordPress means: The redirect comes directly from WordPress.

Turn it off right here…

The reason: WordPress as a “fortune teller”

You never stop learning with WordPress…

Findet WordPress zu einer URL keinen Inhalt, entsteht ein 404 “Seite nicht gefunden” Fehler. Bevor dieser an den Browser ausgeliefert wird, gibt es noch den Aufruf der folgenden Funktion:

redirect_guess_404_permalink()

This function searches the database for a published post whose slug begins with the query variable.

Here's how you do something like that, in a nutshell:

SELECT ID FROM wp_posts
WHERE post_name LIKE 'vouchers%'
  AND post_status = 'publish'
  AND post_type IN (/* Public post types */);Code language: JavaScript (javascript)

If there's a match, WordPress redirects there using a 301 redirect. The slug gift-certificates-for-singing-workshop begins with gift certificates.

The redirect isn't explicitly saved. WordPress recalculates it every time a page is loaded. That's why you can search here until you're blue in the face.

The Guess Redirect only works if the permalink structure is set to something other than “Simple” (see above). The rewrite rules for complex permalinks convert /gift-certificates/ the query variable “name=coupons” and this is then used in the database query. In the "Simple" mode, the post IDs are used, and there is no query variable.

Non-deterministic predictions in forwarding are problematic

In the age of LLMs, probabilities have become the norm instead of exact sequences; however, this does not change the fact that redirects—which sometimes occur and sometimes do not, and are not clearly defined anywhere—impair functionality in various ways.

For example, the query above does not specify a sort order. If there are multiple matching posts, the database determines which one appears first. This means that over the course of a website’s lifetime, the redirect may change internally—specifically, when a new, even more similar post is published that appears higher in the sort order.

From our perspective, this is a classic example of “Well-intentioned is the opposite of good.” If I make a typo in the URL, I want to see a specific error message—“404 Page Not Found”—and not be redirected to the next available page.

And when you have to debug the whole thing on top of that, it gets super annoying—and in this case, it took us a really long time to figure it out. :-/

What else is there: The “Old Slug Redirect”

WordPress has a second, similar mechanism for internal redirects.

If you change a post's slug, WordPress saves the old one in wp_postmeta under _wp_old_slug and redirects the old URL. This redirect is also implicit, but at least it performs an exact match rather than a partial string match. You can test this as follows:

WP DB query "SELECT post_id, meta_value FROM $(wp db prefix)postmeta WHERE meta_key = '_wp_old_slug' AND meta_value = 'gutscheine';"Code language: JavaScript (javascript)

If you're facing a similar problem: Diagnosis in three steps

  1. Check the header. It says there x-redirect-by: WordPress, the Core is responsible. If not, it's a plugin, or it's specified directly in the .htaccess file.
  2. Exclude Old-Slug using the query above.
  3. Disable installments on a trial basis (see below) and again curl Execute.
  4. If you get a 404 now, the guess redirect is confirmed.

Here's how to turn off the WordPress redirect indicator:

<?php
add_filter('do_redirect_guess_404_permalink', '__return_false');Code language: HTML, XML (xml)

Or, if you only want to allow exact matches:

<?php
add_filter('strict_redirect_guess_404_permalink', '__return_true');Code language: HTML, XML (xml)