Redirect map generator
You have finished the migration and you have 300 old URLs with their new destinations in a spreadsheet. Paste the two columns and get the rules for your stack — and, more usefully, a warning when the map contains a chain (A → B → C) or a loop, which are the two mistakes that make redirects fail after they are live.
How to use
- Paste your pairs. Copying two columns out of a spreadsheet gives tab-separated text, which works as-is; commas, arrows and plain spaces are accepted too. The canonical grouper and URL list diff both export in this shape.
- Pick your Output format. The filename in the download button changes to match.
- Leave Use paths only on when the redirect stays on the same host — most server directives match on the path, and a full URL in the match position will simply never fire.
- Read the status line. Chains, loops and duplicate sources are reported there before you deploy them.
- Copy or download, deploy, then test — a redirect map that has not been tested is a guess.
Choosing the status code
301 for anything permanent: it is what transfers ranking signals to the new URL and what browsers cache aggressively. That caching is also the risk — a wrongly-deployed 301 stays in visitors' browsers for a long time and you cannot recall it. If you are unsure whether the move is permanent, ship a 302, verify, and change it to 301 afterwards.
307 and 308 preserve the request method and body. A 301 or 302 on a POST is allowed by browsers to become a GET, which silently loses form data — so for API endpoints and form targets, use 308 (permanent) or 307 (temporary).
Chains, loops and the rules that bite
Chains. If A → B and B → C are both in the map, a visitor to A takes two hops. Each hop costs a round trip, some crawlers stop following after a handful, and signals dilute. The fix is to flatten: point A directly at C. This tool detects two-step chains in your map and names them.
Loops. A → A, or A → B → A, produces ERR_TOO_MANY_REDIRECTS and a page that is simply unreachable. Self-redirects are usually a copy-paste error in a spreadsheet; the check catches them.
Prefix versus exact matching. Apache's Redirect (mod_alias) matches a path prefix, so Redirect 301 /blog /articles also redirects /blogging, appending the remainder. RewriteRule with ^…$ anchors is exact, which is why it is the default output here. nginx's location = is exact for the same reason.
Query strings. Apache's RewriteRule does not see the query string in its pattern — you need RewriteCond %{QUERY_STRING} for that. nginx's $request_uri does include it. Netlify and Vercel match the path and pass query strings through by default. The Include query strings option puts them in the match for formats where that works; if your redirects genuinely depend on parameters, verify each one against your server's documentation rather than trusting a generated rule.
Order and specificity. Apache and Netlify take the first matching rule, so specific rules must come before general ones. nginx picks the most specific location regardless of order. Same map, opposite requirements — worth knowing which stack you are on before assuming the output order is safe to shuffle.
FAQ
How many redirects can I put in .htaccess?
Technically thousands, practically a few hundred before it hurts: .htaccess is read and parsed on every request, and every rule is evaluated in order. Past that, move them into the main server config (parsed once at startup), use nginx's map — which is a hash lookup rather than a scan — or handle them in your application against a database.
Which nginx format should I use?
location = blocks for a handful of redirects: readable and exact. The map directive for hundreds — it is a single hash lookup regardless of size, and it goes in the http block with a small if in the server block, both of which the output shows.
Can I redirect a whole directory in one rule?
Not from a pair list — that is a pattern rule, and each stack writes it differently. Generate the individual pairs here, or write the wildcard rule by hand: Netlify supports /old/* /new/:splat 301!, and Vercel supports :path* in a source.
How do I test the result?
curl -I https://example.com/old-page and check the Location header and status. Do it before removing the old pages, and do it for a sample from every rule shape rather than just the first one.