Indexing and technical SEO guide
Find and Fix Redirect Chains and Loops in WordPress
Trace every hop with curl, identify whether the host, CDN, WordPress, or a plugin sent each one, and fix that layer so every URL reaches its final page in one step.
On this page
Trace the full journey of the URL with curl, write down every hop’s status code and location, and work out which layer sent each one: the CDN, the web server, WordPress core, or a plugin. Then change the layer that adds the extra hop or sends the request back where it came from, and point every rule straight at the final URL. Retest all the variants of the address, not just the one in the report.
This guide handles redirects that fight each other across layers. Creating a single rule is covered in Redirect a Changed URL.
Page with redirect is not an error
Two reasons in the Page Indexing report involve redirects, and only one is a fault. Both descriptions are from Google’s Page Indexing report help, checked September 27, 2026.
- Page with redirect: “This is a non-canonical URL that redirects to another page. As such, this URL will not be indexed. The target URL of the redirect might or might not be indexed, depending on what Google thinks about that target URL.” Old slugs,
http://addresses, and the non-preferredwwwform belong here. Check that the target is right; otherwise leave them. - Redirect error: Google hit “a redirect chain that was too long,” “a redirect loop,” “a redirect URL that eventually exceeded the max URL length,” or “a bad or empty URL in the redirect chain.” This one needs fixing.
Google’s HTTP status documentation, checked September 27, 2026, says its crawlers “follow up to 10 redirect hops” by default. You do not need to get close to that for a chain to be worth fixing: every extra hop is another request for a visitor, and Google’s crawl budget guide, checked September 27, 2026, says to avoid long redirect chains, “which have a negative effect on crawling.”
Trace every hop
Have curl follow the redirects and print only the lines that matter:
curl -sIL --max-redirs 15 http://garden.example/Pruning-Guide | grep -i "^HTTP\|^location\|^x-redirect-by\|^server"
Run it for each variant a visitor or an old link might use:
for u in http://garden.example/pruning-guide/ http://www.garden.example/pruning-guide/ \
https://garden.example/pruning-guide https://www.garden.example/pruning-guide/; do
echo "== $u"; curl -sIL --max-redirs 15 "$u" | grep -i "^HTTP\|^location"
done
Here is illustrative output for a chain. It is an example of the shape to look for, not a capture from a real site:
HTTP/1.1 301 Moved Permanently
location: https://garden.example/Pruning-Guide
HTTP/2 301
location: https://www.garden.example/pruning-guide
x-redirect-by: WP Visibility
HTTP/2 301
location: https://www.garden.example/pruning-guide/
x-redirect-by: WordPress
HTTP/2 200
Three hops, from three different layers. A loop looks like the same location values repeating until curl reaches the --max-redirs limit. Save the output before you change anything; it is your rollback reference.
Identify which layer answered each hop
| Layer | Clues in the response | Where the rule lives |
|---|---|---|
| CDN or proxy | Headers from the CDN vendor; the hop happens even when the origin is down | The CDN dashboard: page rules, redirect rules, “always use HTTPS” settings |
| Web server | No x-redirect-by; can be the http to https hop |
.htaccess, nginx configuration, or the host’s control panel |
| WordPress core | x-redirect-by: WordPress |
Settings > General addresses, permalink settings, core canonical redirects |
| A plugin | x-redirect-by naming the plugin, WordPress if the plugin does not set its own, or no header if the plugin omits it |
The plugin’s redirect screen or settings |
The header comes from WordPress’s wp_redirect(), checked September 27, 2026: since WordPress 5.1 it sends X-Redirect-By with a default value of WordPress, and plugins can pass their own name or false to omit it. WP Visibility’s redirects send WP Visibility. Redirects issued by the server or a CDN never pass through WordPress, so WordPress does not add that header to them.
WordPress core issues some redirects on its own. The redirect_canonical() reference, checked September 27, 2026, describes it sending www and non-www requests to one form based on the site URL, and the source code shown on that page also redirects ?p=123 and similar query URLs to pretty permalinks and adds or removes trailing slashes according to the permalink structure, all with a 301.
WordPress chains and loops to look for
HTTPS in one layer, host in another. The CDN or server upgrades http to https, then WordPress changes garden.example to www.garden.example because that is the Site Address (URL). Two hops for every old link. Fix it by making one layer do both in a single redirect, or by making the server rule go straight to the final host.
SSL at a proxy, WordPress unaware. When a proxy or load balancer handles HTTPS and forwards plain HTTP to WordPress, WordPress can think every request is insecure, and a setting or plugin that forces HTTPS then redirects it to https forever. The WordPress HTTPS handbook page, checked September 27, 2026, says that behind a reverse proxy that provides SSL, the settings that force HTTPS “will initially send any requests into an infinite redirect loop” and shows how to recognize the HTTP_X_FORWARDED_PROTO header in wp-config.php:
if ( strpos( $_SERVER['HTTP_X_FORWARDED_PROTO'], 'https' ) !== false ) {
$_SERVER['HTTPS'] = 'on';
}
Only use this when your proxy actually sets that header. Your host can confirm it.
Host rule against WordPress address. The server forces non-www while Settings > General says www. Each side sends the request to the other. Pick one form, set both WordPress Address (URL) and Site Address (URL) to it, and make the server rule agree.
Trailing slash disagreement. A server rule strips the trailing slash, and WordPress adds it back because the permalink structure ends in /. Remove the server rule or change it to match WordPress.
Rules pointing at rules. A migration left /a/ to /b/, and a later change added /b/ to /c/. Or two redirect plugins each hold half of a pair that points back at itself.
If WP Visibility owns your rules, it refuses a non-regex rule whose target leads straight back to its source, saves a rule that points at another non-regex rule’s source with a warning that visitors will be redirected twice, and answers each request with at most one redirect, so a chain of its own rules still costs one hop per rule. When a published post’s slug changes, it points existing rules that targeted the old path at the new one, so they do not chain. These checks cover its redirect rules only: a post’s own Redirect (301) to field is not checked against them, and it does not check rules held by the server, the CDN, or another plugin.
Correct the responsible layer
- Record a rollback point. Save the trace, a copy of
.htaccessor the relevant configuration, and an export or screenshot of plugin and CDN rules. - Decide the one final URL for each page, including protocol, host, and trailing slash.
- Change one layer at a time, starting with the layer that sent the first unwanted hop.
- Point every rule at the final URL. Update
/a/to go directly to/c/, and remove rules that duplicate another layer’s job. - Retest after each change with the loop above, so you know which change fixed or broke what.
- Update internal links and the sitemap to use final URLs, so your own pages do not start chains.
If a loop has locked you out of the WordPress admin, the WordPress migration handbook, checked September 27, 2026, shows how to set the address in wp-config.php with define( 'WP_HOME', 'https://example.com' ); and define( 'WP_SITEURL', 'https://example.com' );. It notes that the General settings fields can no longer be edited while those lines are present, so correct the stored addresses (the same page covers the database and functions.php methods) before you remove them. You can also deactivate the most recent redirect or SSL plugin by renaming its folder in wp-content/plugins over FTP or your host’s file manager, as the Learn WordPress troubleshooting lesson describes, checked September 27, 2026.
Verify the journey
Every variant should reach a 200 in at most one redirect:
== http://garden.example/pruning-guide/
HTTP/1.1 301 Moved Permanently
location: https://www.garden.example/pruning-guide/
HTTP/2 200
Then inspect an affected URL in Search Console and run Test live URL. For “Redirect error,” open the reason in the Page Indexing report and click Validate fix; the Page Indexing report help, checked September 27, 2026, says validation “typically takes up to about two weeks, but in some cases can take much longer.” URLs that now redirect cleanly should move to “Page with redirect,” which is the expected state. For redirects that should instead be 404 or 410 responses, see 404, 410, or redirect; for how redirects feed Google’s canonical choice, see Canonical selection.
