The two failures that actually break a migration
Redirect syntax is easy and well documented, so it is rarely what goes wrong. What goes wrong is the map itself, and it fails in two shapes.
The first is a source listed twice with two different targets. This happens whenever the map is assembled from more than one spreadsheet, or when somebody adds a late correction at the bottom of the file instead of editing the line above. Both rules are syntactically perfect. Only one fires — in Apache, nginx and every host-level redirects file, the first match wins — so the site ends up with the behaviour of whichever line happened to be typed first, and nobody notices until a customer reports that a link goes somewhere strange.
The second is a chain or a loop. A chain is A to B to C: it works, but every visitor and every crawler pays an extra request to get there, and some clients cap how many hops they will follow. A loop is A to B and B back to A, and it does not work at all: the browser stops after a fixed number of hops and shows a redirect error. Loops nearly always come from adding a rule to fix a mistake without deleting the rule that caused it.
Both are properties of the map as a whole, which is why a config file with correct syntax on every line can still be broken. The checks on this page walk the whole map, following each target to see whether it is itself a source.
Which status code
| Code | Meaning | When |
|---|---|---|
| 301 | Permanent | The default for a migration. Clients cache it, sometimes aggressively, and some browsers keep it until the cache is cleared by hand. |
| 308 | Permanent, method preserved | Same as 301 except a POST stays a POST. Use it if the URL can be posted to; 301 historically got rewritten to GET. |
| 302 | Temporary | Genuinely temporary moves — a maintenance page, a seasonal landing page you will take down. |
| 307 | Temporary, method preserved | The modern temporary code, for the same reason 308 exists. |
The practical warning is about 301 caching. Deploy a wrong 301 and visitors who hit it keep going to the wrong place after you fix the config, because the redirect is cached in their browser and your server never gets asked again. That is an unpleasant afternoon. When you are unsure of a mapping, ship it as a 302, confirm it in the logs, then promote it to 301.
Prefix matching, and the rule that eats a subtree
Apache has two directives that look interchangeable and are not. Redirect 301 /about /company matches any path beginning /about, so /about-us/team is caught too and lands on /company-us/team. RedirectMatch takes a regular expression, so anchoring it with ^ and $ gives an exact match. That is what this page writes, with the regex metacharacters in your paths escaped. nginx has the same split: location /about is a prefix, location = /about is exact.
If you genuinely want a whole subtree moved, prefix matching is the right tool and you want one rule rather than a thousand. Just write that rule deliberately rather than discovering it.
Query strings, trailing slashes and case
Neither the Apache nor the nginx form here matches on the query string, because neither directive sees it. A rule for /search fires for /search?q=anything as well. Matching on a query needs RewriteCond %{QUERY_STRING} in Apache or a test on $arg_name in nginx, and it is worth knowing that before you write a map that assumes otherwise.
Trailing slashes are two different URLs as far as a matcher is concerned, which is why there is a checkbox to emit both variants. Paths are case sensitive on most Unix servers, so /About and /about are different too. If your old site was case-insensitive and the new one is not, that is a separate normalising rule and not something a per-path map will catch.
How long to keep them
Longer than feels necessary. Links on other people's sites do not get updated, bookmarks live for years, and old printed material keeps arriving. A redirect map costs almost nothing to keep and the cost of deleting one is a permanent dead link. Prune when the server logs show the old path has gone quiet, not on a calendar.
Once the map is live, the related pieces are the sitemap generator for the new URL list and the robots.txt generator for anything you no longer want crawled. If the new URLs are being invented rather than transcribed, run the titles through the slug generator first so the map is built once. Where a redirect is not the right answer because both URLs should stay live, the canonical and robots tag builder covers that case.
Questions people ask
Why did my duplicate line get dropped instead of merged?
Because there is no sensible merge. Two rules for the same source with different targets are a contradiction, not a pair of instructions, and a server resolves it by taking the first one it matches. This page does the same thing so that what you see is what will happen, and it names both lines so you can go and delete the one you did not mean.
Is a chain actually harmful, or just untidy?
It works, so it is not an outage. It costs an extra request for every visitor who arrives at the first URL, it adds latency on a slow connection where an extra round trip is expensive, and crawlers follow only so many hops before giving up. Collapsing A to B to C into A to C is a one-line edit and there is no reason not to make it.
Can I redirect to a different domain?
Yes — put the full URL in the target column and all three formats will use it as written. Two things change when you cross a domain: the browser makes a fresh DNS and TLS connection, so the hop costs more, and anything relying on cookies or session state on the old host will not carry across. Test a logged-in path as well as a public one.
Will a redirect preserve how the old page ranked?
Nobody outside a search engine can tell you what a redirect does to ranking, and anybody who states a percentage is guessing. What a redirect definitely does is send a person or a crawler that arrived at a dead address to a live one, which is the part you control. Point it at the closest equivalent page rather than the homepage, because a visitor who lands somewhere unrelated leaves, and that is a real effect you can see in your own logs.
What about redirecting in JavaScript or with a meta refresh?
Both work for a human in a browser and both are worse than a server response. A meta refresh delays rendering and gives the client no status code to act on; a JavaScript redirect requires the script to run at all, which is not guaranteed for every client that fetches the page. Use them only when you have no access to the server config, and treat them as a stopgap.