Redirect generator

Paste pairs of old and new URLs, one per line, separated by a comma, a tab or a space. Full URLs are fine - the domain is stripped.

Written because this site's own migration needed 271 of these. Generating them by hand is where the mistakes come from.

The two mistakes this exists to prevent

An unescaped full stop. In a regular expression . matches any character, so a rule written for /pricing.html also matches /pricingXhtml. Harmless until it is not. Every pattern here is escaped.

A rule with no anchor. Apache's Redirect matches by prefix and cannot be anchored, so a rule for /shop also catches /shopping, /shop-faq and everything else beginning the same way. That is the failure you hear about from a customer rather than find yourself. RedirectMatch is anchored with ^ and $, which is why it is the default here.

The generator also refuses a URL that redirects to itself, and tells you when the same source appears twice - in which case the first rule wins and the second is dead weight.

Which style to use

Style Use it when
RedirectMatch Almost always. Anchored, escaped, exact.
Redirect Moving a whole directory, where prefix matching is the point.
RewriteRule You are already inside a mod_rewrite block, or need conditions.

Three things Apache will not do

A query string cannot be matched by a RedirectMatch or RewriteRule pattern. /?p=123 needs a RewriteCond %{QUERY_STRING} before the rule; the pattern itself only ever sees the path.

[L] does not mean "last" in a per-directory .htaccess. An internal rewrite re-runs the whole ruleset from the top, so a rule protected only by [L] can match its own output. Guard it with a RewriteCond instead.

A .htaccess in a subdirectory replaces its parent's rules rather than adding to them. A rule for /cms/... written in the web root simply does not run while WordPress ships its own file at /cms/.htaccess - which is why migration redirects for media so often appear to do nothing until the old install is finally removed.