WDD Redirect Chain Fixer
v1.1.0

WDD Redirect Chain Fixer

Create and manage redirects, find redirect chains, and point every old URL straight at its final destination — faster pages and cleaner SEO.

Redirect manager

301, 302, 307 and 308 redirects with hit tracking and on/off toggles.

Paths or full URLs

Enter /old-page or https://yoursite.com/old-page — both work.

Chain detection

Finds A → B → C chains in your rules, however long, and skips loops.

One-click fix

Fix one chain or all of them; each source points directly to the final URL.

Live URL checker

Trace every real HTTP hop for any URL, on your site or elsewhere.

Under Settings

Lives at Settings → Redirect Chain Fixer, not in the main menu.

Requirements

RequirementVersion
WordPress5.1 or later
PHP7.1 or later
User capabilitymanage_options — administrators only

Installation

  1. Go to Plugins → Add New → Upload Plugin and upload wdd-redirect-chain-fixer-1.1.0.zip, or copy the wdd-redirect-chain-fixer folder to /wp-content/plugins/.
  2. Activate the plugin from the Plugins screen.
  3. Open Settings → Redirect Chain Fixer, or click Settings under the plugin's name on the Plugins screen.

On activation the plugin creates its own table, {prefix}wdd_redirects.

Updating from 1.0.0

Upload the new ZIP and choose Replace current with uploaded. On the first page load after the update, the plugin converts any full-URL sources you saved earlier to paths so they start working. Take a database backup first.

The menu has moved

In 1.0.0 the plugin had a top-level Redirect Fixer menu. From 1.1.0 it is under Settings. Old bookmarks to admin.php?page=wdd-redirect-chain-fixer are forwarded automatically.

Quick start

  1. Go to Settings → Redirect Chain Fixer → Add Redirect.
  2. Enter the Source URL — the old address, e.g. /old-page or https://yoursite.com/old-page.
  3. Enter the Target URL — where visitors should go, e.g. /new-page or https://othersite.com/page.
  4. Pick a Redirect Type (301 for permanent moves) and leave Active on.
  5. Click Save Redirect, then visit the old URL in a private window to confirm.

Source & target URLs

Source URL

The address that should redirect. It must be on your own site. All of these save as the same rule, /old-page:

/old-page
old-page
https://yoursite.com/old-page
http://www.yoursite.com/old-page/
//yoursite.com/old-page
  • The domain is removed on save — http/https and www/non-www don't matter.
  • A full URL on another domain is rejected, because requests for that domain never reach your site.
  • Query strings (?id=5) are not supported in sources and are rejected with an error.
  • Percent-encoded characters are decoded, so /caf%C3%A9 and /café are the same rule.

Target URL

Where visitors are sent. It can be a path on your site or any full URL, and is stored exactly as you type it (case and query string included).

You enterVisitors go to
/new-pagehttps://yoursite.com/new-page
new-pagehttps://yoursite.com/new-page
https://yoursite.com/New-Page?ref=oldExactly that URL
https://othersite.com/pageExactly that URL
Sites installed in a subdirectory

Paths are always measured from the domain root. If WordPress lives at https://example.com/blog/, write /blog/old-page and /blog/new-page — or simply paste the full URLs.

Redirect types

CodeMeaningUse when
301Moved PermanentlyA page has moved for good. Passes SEO value. The default.
302Found (temporary)A short-term move, such as a promotion.
307Temporary RedirectTemporary, and the request method (e.g. POST) must be kept.
308Permanent RedirectPermanent, and the request method must be kept.

The default for new redirects is set under Settings.

How matching works

On each frontend request the plugin takes the request path, drops the query string, decodes it, and looks it up among active rules:

  • Trailing slash insensitive — /old-page and /old-page/ both match the rule.
  • Case insensitive by default — /Old-Page matches /old-page. Turn on case-sensitive matching in Settings to change this.
  • Query strings ignored — /old-page?utm_source=x matches /old-page. The query string is not carried over to the target.
  • No self-redirects — a rule whose target resolves to its own source is never fired.

Redirects run early on template_redirect, before WordPress's own canonical redirects, and are skipped for admin, REST, cron and WP-CLI requests. Responses carry an X-Redirect-By: WDD Redirect Chain Fixer header so you can tell them apart from other redirects.

Managing redirects

The Redirects tab lists every rule with its source, target, type, hit count and status.

  • Search by source or target, and filter by active / inactive.
  • Toggle a rule on or off instantly with its status switch.
  • Edit or delete with the row icons.
  • Bulk actions — activate, deactivate or delete selected rules.

Each source can have only one rule. Saving a duplicate, a source on another domain, or a source equal to its target shows an error on the form so you can correct it.

What is a redirect chain?

A chain happens when a redirect's target is itself redirected. Each extra hop costs the visitor a round trip and can dilute SEO value.

/page-a→ /page-b→ /page-c

Fixing it updates the first rule so it goes straight to the end:

/page-a→ /page-c

Rule /page-b → /page-c is kept, so anyone who visits /page-b still arrives.

Chain Scanner

The Chain Scanner tab lists chains found among your active rules, showing the source, every intermediate URL, and the final destination.

  • Fix — points that one source at the final URL.
  • Fix All Chains — fixes every chain in one pass. Also available as Auto-Fix All on the Dashboard.
  • Chains of any length are followed to the end.
  • Paths and full URLs on your site are matched together, so /a → https://yoursite.com/b plus /b → /c is detected.
  • Loops (/a → /b → /a) are not listed, because there is no final URL. Edit one of the rules by hand.
Chains outside the plugin

The scanner only sees rules stored in this plugin. Redirects from your server, another plugin or WordPress itself (e.g. changed slugs) are visible with the live URL checker.

Live URL checker

At the top of the Chain Scanner tab, enter any full URL and click Check URL. The plugin requests it from your server and follows each redirect by hand, up to 10 hops, listing every URL with its HTTP status.

ResultMeaning
Clean — no redirectThe URL answers directly.
Clean — single direct redirectOne redirect straight to the destination. This is fine.
Redirect chain detectedTwo or more redirects in a row. The unnecessary hops are counted.
Loop detectedThe same URL came back; the trace stops.

It uses a HEAD request and falls back to GET if the server rejects HEAD. Relative Location headers are resolved correctly.

Settings

Found on the Settings tab inside Settings → Redirect Chain Fixer.

SettingDefaultWhat it does
Default Redirect Type301Pre-selected type when adding a redirect.
Track Hit CountOnCounts each time a redirect fires and records the last access time.
Case-Sensitive URL MatchingOffWhen off, /Old-Page and /old-page trigger the same rule. Turning it off lowercases existing sources so they keep matching.

Troubleshooting

My redirect doesn't fire

  1. Check the rule is Active.
  2. Test in a private window — browsers cache 301 redirects aggressively.
  3. Purge your page cache or CDN. A cached copy of the old page is served without WordPress running.
  4. Make sure the old URL isn't an existing file or folder on the server; those are served before WordPress loads.
  5. On a subdirectory install, include the subdirectory in the path (see Source & target URLs).

"The source URL must be on this site"

The domain in the source doesn't match your Site Address (Settings → General). Use the path only, or correct the domain.

"Source URLs cannot include a query string"

Remove everything from ? onward. Requests with any query string still match the path.

I saved full-URL redirects in 1.0.0 and they never worked

Update to 1.1.0. They are converted to paths automatically on the first page load after updating.

The Chain Scanner says there are no chains, but the live checker finds one

The extra hop comes from outside this plugin — your server config, another plugin, or WordPress's old-slug redirect. Add a rule here from the original URL straight to the final one.

Uninstalling

ActionResult
DeactivateRedirects stop firing. Your rules and settings are kept.
DeleteThe {prefix}wdd_redirects table and all plugin options are permanently removed.
Deleting removes your redirects

Old URLs will return 404 once the plugin is deleted. Back up the database first if you might need the rules again.

File structure

wdd-redirect-chain-fixer/
├── wdd-redirect-chain-fixer.php          Plugin header, constants, bootstrap
├── uninstall.php                         Cleanup on plugin deletion
├── readme.txt                            WordPress readme and changelog
├── CHANGELOG.md                          Developer changelog
├── docs/
│   └── index.html                        This documentation
├── includes/
│   ├── class-wdd-rcf-activator.php       Table creation, defaults, upgrade routine
│   ├── class-wdd-rcf-deactivator.php
│   ├── class-wdd-rcf-database.php        Queries, URL normalization, chain detection
│   ├── class-wdd-rcf-redirect-handler.php Frontend redirect matching
│   └── class-wdd-rcf-chain-detector.php  Chain fixing and live URL tracing
└── admin/
    ├── class-wdd-rcf-admin.php           Settings page, forms, AJAX handlers
    ├── css/wdd-rcf-admin.css
    └── js/wdd-rcf-admin.js

Database table

ColumnTypeNotes
idBIGINT, auto-incrementPrimary key
source_urlVARCHAR(2000)Normalized path, e.g. /old-page
target_urlVARCHAR(2000)Path or full URL, stored as entered
redirect_typeSMALLINT301, 302, 307 or 308
hit_countBIGINTTimes the redirect fired
last_accessedDATETIMELast time it fired
is_activeTINYINT(1)1 active, 0 inactive
notesTEXTOptional
created_at, updated_atDATETIME

Changelog

Follows Keep a Changelog and Semantic Versioning.

1.1.0

Latest

Fixes full-URL source redirects, moves the plugin under Settings, and makes chain detection follow complete chains.

Fixed

  • Full-URL sources did not redirect. https://yoursite.com/old-page was stored as typed but compared against the request path /old-page, so it never matched. Full URLs on your domain are now reduced to their path, and existing rules are migrated on update.
  • Target URLs were lowercased, breaking case-sensitive destinations and query strings. Targets are now stored exactly as entered.
  • Percent-encoded URLs were mangled because sanitize_text_field() strips %xx. Stored URLs and request paths are now decoded consistently.
  • Chain detection was incomplete. It missed chains mixing full URLs and paths and only reported two steps. Chains are now followed to the end, and loops are skipped instead of being turned into self-redirects.
  • Editing a redirect could create a duplicate source.
  • The same-URL check now compares paths, so /old and https://yoursite.com/old/ count as the same.
  • The live checker counted a single redirect as a chain, resolved relative Location headers wrongly, and didn't fall back to GET on 403/405/501.
  • Turning case-sensitive matching off left mixed-case sources unmatched.
  • dbDelta() was given CREATE TABLE IF NOT EXISTS, which it cannot parse. Upgrades now also run after in-place updates.
  • Request paths starting with // could be misread as a host name.

Changed

  • Admin page moved from the main menu to Settings → Redirect Chain Fixer; old links are forwarded.
  • Relative targets resolve against the site's scheme and host, consistent with source matching. Subdirectory installs should include the subdirectory.
  • Form errors return to the Add/Edit form instead of the list.
  • A rule whose target equals its own source is never fired.
  • Redirects send an X-Redirect-By header.
  • Requires WordPress 5.1+ and PHP 7.1+.

Added

  • Settings link on the Plugins screen.
  • Clear errors for sources on another domain or with a query string.

1.0.0

Initial release

Added

  • Redirect manager with 301, 302, 307 and 308 redirects.
  • Hit tracking and last-accessed time.
  • Database chain detection with one-click and bulk fixing.
  • Live URL chain checker.
  • Search, status filter, toggles and bulk actions.
  • Case-sensitive matching setting.