A high-traffic URL on a WordPress site I maintain started throwing a warning in the crawl report: one page was going through three back-to-back 301s before it landed.
/podcasts → /resources/ → /podcast → /podcast/
Four hops, three redirects. Over 9,000 hits ran through this chain. Bad for SEO (link equity bleeds at every hop), bad for crawl budget, and slow for users. The site owner already had a theory: "There's no redirect plugin, so it must be .htaccess." Her reasoning was simple — she couldn't find a redirect menu where she expected one to live.
She was wrong. And that mistake is the single most important lesson from this incident: redirect chains almost always have multiple owners.
Why everyone blames .htaccess first
.htaccess is the obvious suspect because it's one readable text file. But on a modern WordPress install, redirects can come from several places at once, and some of them don't appear under the Settings menu at all:
- Redirection plugin — lives under the Tools menu, not Settings. That's exactly why the owner couldn't find it.
- Rank Math → Redirections — buried inside the SEO menu, a separate module you have to enable.
- WordPress core canonical redirect — enforces trailing slashes (
/podcast→/podcast/) automatically. - WP core
_wp_old_slug— when you change a post's slug, WordPress stores the old slug as post meta and auto-redirects from those previous slugs.
A chain forms when one mechanism's target is itself redirected by another. For example: the Redirection plugin sends /podcasts to /resources/; but /resources/ is an old page whose slug was once changed to podcast, so _wp_old_slug bounces it to /podcast; then core canonical appends the trailing slash to /podcast/. Three different owners, one chain.
Audit EVERY source first — touch nothing yet
Before agreeing with the "it's .htaccess" theory, I audited every source that could possibly own a redirect:
- Tools menu → Redirection (the Redirection plugin)
- SEO menu → Rank Math → Redirections
wp_options— permalink settings and plugin options_wp_old_slugpost meta — each post's old-slug history.htaccess— manual mod_rewrite rules- Theme
functions.php— home-grownwp_redirect()/template_redirecthooks
Rule #1: map every hop to its owner before changing a single thing.
You can read the Redirection plugin's rules through its REST API without opening the UI:
# Read all Redirection plugin rules
curl -s "https://your-site.com/wp-json/redirection/v1/redirect" \
-H "X-WP-Nonce: <nonce>" --cookie "wordpress_logged_in_..."Rank Math's rules have to be edited in its UI (there's no convenient read endpoint), so open SEO → Redirections manually.
For _wp_old_slug, check the database directly:
SELECT post_id, meta_value
FROM wp_postmeta
WHERE meta_key = '_wp_old_slug';Fix at the SOURCE of each hop — don't stack new redirects
The biggest temptation when you see /podcasts land on /podcast/ is to create one new rule: /podcasts → /podcast/. Don't. That just adds a fifth owner and leaves the old three hops alive for other URLs.
The right move: for each hop, identify the mechanism that owns it, then fix it at the source of that intermediate hop so the chain collapses to a single 301:
- Change the Redirection rule's target from
/resources/directly to/podcast/(the final destination, with the correct trailing slash) — this kills two hops at once. - Clean up stale
_wp_old_slugentries if they're no longer needed, so core stops bouncing. - Make sure your rule already writes the canonical form (
/podcast/) so core canonical doesn't have to add a trailing-slash hop of its own.
Verify: one hop, not three
After fixing, prove the chain actually collapsed to a single 301. From the browser:
// .url is the final URL after all redirects are followed
fetch("https://your-site.com/podcasts", { redirect: "follow" })
.then(r => console.log(r.url));Or count the 301s from the command line:
# -I header only, -L follow redirects, -s silent
curl -sIL "https://your-site.com/podcasts" | grep -i "^HTTP\|^location"What you want to see: exactly one 301 line, then 200. If you still see two or three 301s, there's a hop whose owner you haven't fixed yet.
Checklist for when you find a redirect chain
- Don't trust "there's no redirect plugin" — check the Tools menu and the SEO menu, not just Settings.
- Map every hop to its owner: Redirection, Rank Math, core canonical,
_wp_old_slug,.htaccess,functions.php. - Read Redirection rules via the REST API (
/wp-json/redirection/v1/redirect); Rank Math via the UI. - Fix at the source of the intermediate hop so the chain collapses to a single 301 — don't stack a new rule on top.
- Verify with
fetch(...).urlorcurl -sILand confirm there's only one 301.
The lesson: redirect chains are usually multi-owner. Never assume there's a single source. Map every hop to its owner first — then touch anything.
