A customer told us that the short links they share for their blog posts stopped working on phones. Tapping one showed “Could Not Follow Redirect” and nothing else. Our uptime monitoring was green and most of the site opened fine in a desktop browser, so at first we looked at the link shortener, but it was working correctly. The links pointed to posts with Cyrillic slugs, and WordPress was redirecting those posts to themselves over and over.
What we saw
We reproduced it with curl, using a normal GET request and a limit on how many redirects to follow:
curl -sIL -X GET "https://example.com/%D0%BD%D0%BE%D0%B2%D0%B8%D0%BD%D0%B8/" --max-redirs 5
Every response was a 301 pointing to exactly the same URL, until curl stopped with “Maximum redirects followed”. A plain curl -I returned 200, because it sends a HEAD request, and HEAD didn’t loop. That is also why our uptime checks, which use HEAD, never noticed anything. Browsers, in-app browsers and QR scanners all send GET, so every real visitor hit the loop. Pages with Latin slugs and the home page were not affected, only the posts whose URLs contained encoded Cyrillic characters.
The cause
The redirect came from WordPress itself, from the code that sends visitors to the canonical version of a URL. It had a wrong decision about the encoded Cyrillic URL stored in the object cache, which is Redis on our platform, and it kept sending the URL to itself.
We lost some time here because the page cache in front of the site reported BYPASS on every request, and we took that as proof that caching had nothing to do with it. That header only describes the page cache, though. The object cache lives inside WordPress, and nothing in the response tells you what it contains.
The fix
Purging the page cache on its own changed nothing. Purging everything, including the WordPress object cache, fixed the loop straight away, and the short links started working on phones again.
To make sure it can’t come back, a small must-use plugin can refuse any canonical redirect that points to the same address as the request:
<?php
// wp-content/mu-plugins/no-self-redirect.php
add_filter('redirect_canonical', function ($redirect, $requested) {
return rawurldecode($redirect) === rawurldecode($requested) ? false : $redirect;
}, 10, 2);
It compares the decoded URLs, so the encoded and decoded forms of the same Cyrillic slug count as one page, and normal canonical redirects such as adding a trailing slash keep working.
If your site has URLs with non-Latin characters, test a few of them with GET requests and not only HEAD.