{"id":39787,"date":"2026-09-21T19:06:50","date_gmt":"2026-09-21T17:06:50","guid":{"rendered":"https:\/\/mainetcare.de\/?post_type=gartenschuppen&#038;p=39787"},"modified":"2026-09-22T10:48:58","modified_gmt":"2026-09-22T08:48:58","slug":"why-does-wordpress-redirect-urls-when-permalinks-are-enabled","status":"publish","type":"gartenschuppen","link":"https:\/\/mainetcare.de\/en\/gartenschuppen\/warum-leitet-wordpress-urls-um-wenn-permalinks-aktiv-sind\/","title":{"rendered":"Why does WordPress automatically redirect URLs when permalinks are enabled?"},"content":{"rendered":"<h2 class=\"wp-block-heading\">Symptom<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">We encountered this issue on a client's website: A URL like <code>\/gift-certificates\/<\/code> landed at 301 <code>\/<\/code>xxx<code>\/gift-certificates-for-singing-workshop\/<\/code>.<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>No redirect plugin was active, <\/li>\n\n\n\n<li>in the <code>.htaccess<\/code> And there were no redirects in the nginx configuration\u2014nothing.<\/li>\n\n\n\n<li>All plugins were disabled<\/li>\n\n\n\n<li>The redirection persisted.<\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">If you set the permalinks to \u201eSimple\u201c\u2014that is, in the format example.test\/?p=123 instead of example.test\/123\/\u2014this issue does not occur.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The HTTP header clearly points to WordPress as the source:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">curl -skI https:<span class=\"hljs-comment\">\/\/example.test\/coupons\/<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">provides:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"HTTP\" data-shcb-language-slug=\"http\"><span><code class=\"hljs language-http\">HTTP\/2 <span class=\"hljs-number\">301<\/span>\n<span class=\"hljs-attribute\">location<\/span>: https:\/\/example.test\/singen-lernen-shop\/gutscheine-fuer-gesangs-workshop\/\n<span class=\"hljs-attribute\">x-redirect-by<\/span>: WordPress<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTTP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">http<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\"><code>x-redirect-by: WordPress<\/code> means: <strong>The redirect comes directly from WordPress.<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"#guess-redirect-direkt-abschalten\">Turn it off right here\u2026<\/a><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The reason: WordPress as a \u201cfortune teller\u201d<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">You never stop learning with WordPress\u2026 <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Findet WordPress zu einer URL keinen Inhalt, entsteht ein 404 &#8220;Seite nicht gefunden&#8221; Fehler. Bevor dieser an den Browser ausgeliefert wird, gibt es noch den Aufruf der folgenden Funktion:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong><code>redirect_guess_404_permalink()<\/code> <\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This function searches the database for a published post whose slug begins with the query variable.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Here's how you do something like that, in a nutshell:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">SELECT ID FROM wp_posts\nWHERE post_name LIKE <span class=\"hljs-string\">'vouchers%'<\/span>\n  AND post_status = <span class=\"hljs-string\">'publish'<\/span>\n  AND post_type IN (<span class=\"hljs-comment\">\/* Public post types *\/<\/span>);<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">If there's a match, WordPress redirects there using a 301 redirect. The slug <code>gift-certificates-for-singing-workshop<\/code> begins with <code>gift certificates<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">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.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The Guess Redirect only works if the permalink structure is set to something other than \u201cSimple\u201d (see above). The rewrite rules for complex permalinks convert <code>\/gift-certificates\/<\/code> the query variable \u201c<code>name=coupons<\/code>\u201d and this is then used in the database query. In the \"Simple\" mode, the post IDs are used, and there is no query variable.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Non-deterministic predictions in forwarding are problematic<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">In the age of LLMs, probabilities have become the norm instead of exact sequences; however, this does not change the fact that redirects\u2014which sometimes occur and sometimes do not, and are not clearly defined anywhere\u2014impair functionality in various ways.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">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\u2019s lifetime, the redirect may change internally\u2014specifically, when a new, even more similar post is published that appears higher in the sort order.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">From our perspective, this is a classic example of \u201cWell-intentioned is the opposite of good.\u201d If I make a typo in the URL, I want to see a specific error message\u2014\u201c404 Page Not Found\u201d\u2014and not be redirected to the next available page.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">And when you have to debug the whole thing on top of that, it gets super annoying\u2014and in this case, it took us a really long time to figure it out. :-\/<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">What else is there: The \u201cOld Slug Redirect\u201d<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">WordPress has a second, similar mechanism for internal redirects.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If you change a post's slug, WordPress saves the old one in <code>wp_postmeta<\/code> under <code>_wp_old_slug<\/code> 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:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">WP DB query <span class=\"hljs-string\">\"SELECT post_id, meta_value FROM $(wp db prefix)postmeta WHERE meta_key = '_wp_old_slug' AND meta_value = 'gutscheine';\"<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h2 class=\"wp-block-heading\">If you're facing a similar problem: Diagnosis in three steps<\/h2>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Check the header.<\/strong> It says there <code>x-redirect-by: WordPress<\/code>, the Core is responsible. If not, it's a plugin, or it's specified directly in the .htaccess file.<\/li>\n\n\n\n<li><strong>Exclude Old-Slug<\/strong> using the query above.<\/li>\n\n\n\n<li><strong>Disable installments on a trial basis<\/strong> (see below) and again <code>curl<\/code> Execute. <\/li>\n\n\n\n<li>If you get a 404 now, the guess redirect is confirmed.<\/li>\n<\/ol>\n\n\n\n<h2 id=\"guess-redirect-direkt-abschalten\" class=\"wp-block-heading\">Here's how to turn off the WordPress redirect indicator:<\/h2>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"php\"><span class=\"hljs-meta\">&lt;?php<\/span>\nadd_filter(<span class=\"hljs-string\">'do_redirect_guess_404_permalink'<\/span>, <span class=\"hljs-string\">'__return_false'<\/span>);<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Or, if you only want to allow exact matches:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"php\"><span class=\"hljs-meta\">&lt;?php<\/span>\nadd_filter(<span class=\"hljs-string\">'strict_redirect_guess_404_permalink'<\/span>, <span class=\"hljs-string\">'__return_true'<\/span>);<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\"><\/p>","protected":false},"featured_media":0,"template":"","meta":{"_acf_changed":false},"class_list":["post-39787","gartenschuppen","type-gartenschuppen","status-publish","hentry"],"acf":[],"_links":{"self":[{"href":"https:\/\/mainetcare.de\/en\/wp-json\/wp\/v2\/gartenschuppen\/39787","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/mainetcare.de\/en\/wp-json\/wp\/v2\/gartenschuppen"}],"about":[{"href":"https:\/\/mainetcare.de\/en\/wp-json\/wp\/v2\/types\/gartenschuppen"}],"wp:attachment":[{"href":"https:\/\/mainetcare.de\/en\/wp-json\/wp\/v2\/media?parent=39787"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}