What each platform file does
- Apache (.htaccess). Per the mod_alias docs,
Redirectmatches by prefix and appends the rest of the path, soRedirect 301 /old /newalso sends /old/page to /new/page. We default toRedirectMatch 301 ^/old/?$ /newwith the path regex-escaped, which matches only that page. Prefix mode is there for moving whole sections. - Nginx. One
location = /old { return 301 /new; }per rule, for theserverblock.returndoesn’t append the query string, hence$is_args$args. Runnginx -tbefore reloading. - Vercel and Next.js.
permanent: truesends 308 andfalsesends 307;statusCodegives a literal 301 or 302, never both on one entry (Vercel, Next.js). The characters ( ) { } : * + ? have meaning insource, so we escape them. Vercel caps configuration redirects at 2,048; above that, use the bulk CSV withbulkRedirectsPath. - Netlify (_redirects).
/old /new 301, first match wins, and the docs list 301 and 302 (307 is unsupported, so we map it). Add!to redirect even when a file still exists at the old path. - Cloudflare Bulk Redirects. A CSV with no header row: source, target, status, preserve query string (format docs). Sources are host plus path with no scheme, so they match http and https.
Checks before you ship
Sources with a query string or #fragment are skipped, since path rules can’t match them. Duplicate sources are merged, and conflicting ones keep the first rule: most platforms stop at the first match, and Nginx refuses duplicate locations outright. A rule that points at itself is a loop and is dropped, as is any cycle like A→B→A. Chains are reported and, with flattening on, collapsed to one hop.
After deploying
Test a few old URLs with the redirect checker to see every hop and the final status. Then update internal links and your sitemap to the new URLs, so crawlers don’t keep requesting old ones: the broken link checker and the XML sitemap generator help. Google’s redirects guide lists 301 and 308 together as “moved permanently”, so either is fine for a page that has moved for good.