Redirect mapping: how to do it, with a copyable template
A redirect map pairs every old URL with its new home and a status code. How to build one, the matching rules, a CSV template and a script to test it.
By Sohail Hussain9 November 20266 min read
A redirect map is a spreadsheet with one row per old URL: the new URL it should send visitors to, and the status code to use. You build it in seven steps: collect every old URL, add traffic and link data, match each URL to its new page, decide what happens to pages with no match, check for chains, turn the rows into server rules, and test every row after launch. The template below has the columns we use. Copy it into a spreadsheet and start with your highest-traffic pages.
The redirect map is the single document that decides whether a redesign keeps its rankings. It sits at the centre of every website redesign and migration we run, and it is stage five of the website redesign process.
What goes in a redirect map?
Here is the template, with example rows. Save it as redirect-map.csv and open it in Google Sheets or Excel.
old_path,new_path,status,match,clicks_12m,linking_domains,priority,tested
/about-us.html,/about,301,same page,1240,18,high,
/services/web-design-pune,/services/website-design,301,same page,3410,9,high,
/services/logo-design,/services/branding,301,merged,310,2,medium,
/blog/2019/05/redesign-tips,/blog/website-redesign-process,301,closest topic,220,3,medium,
/careers/summer-intern-2021,/careers,301,parent page,12,0,low,
/offer-2022,,410,removed,0,0,low,
/downloads/brochure-2023.pdf,/downloads/company-brochure.pdf,301,file moved,85,6,high,
/contact,/contact,200,unchanged,960,4,high,
| Column | What goes in it | Why it is there |
|---|---|---|
old_path | The path exactly as it exists today, starting with /, including any trailing slash, capitals or query string | Redirects match exact paths |
new_path | The final destination path, empty for 404 or 410 | One hop, never via another redirect |
status | 301 (or 308), 404, 410, or 200 for unchanged URLs | Unchanged rows prove the URL was checked, not forgotten |
match | same page, merged, closest topic, parent page, removed, file moved | Lets a second person review each decision |
clicks_12m | Search Console clicks over the last twelve months | Sets the order of work and testing |
linking_domains | Number of other sites linking to the URL | Links are why some zero-traffic pages still matter |
priority | high, medium or low | Decides what is tested by hand first |
tested | Date and result after launch | The record that the redirect works |
Keep paths, not full URLs, in the first two columns. If the domain is also changing, that is one extra rule on top; see the domain change checklist.
Where do you find every old URL?
Step 1: Collect every old URL
Pull URLs from every source you have, paste them into the old_path column, then remove duplicates:
- The XML sitemap.
- Search Console's Performance report (pages with impressions) and Pages report. The interface caps exports, so large sites need the API or a crawler.
- Analytics landing pages for the last twelve months.
- Server logs, which show URLs still requested by people and bots.
- The redirects already on the site. Old links pointing at them still exist.
- A crawl of the live site, which also finds images and PDFs.
Strip tracking parameters such as utm_source, but keep query strings that select content, like /products?id=42.
Step 2: Add traffic and link data
Add clicks per page from Search Console and linking sites from the Links report ("Top linked pages"). Then set priority: high for the pages that bring most of your clicks and for any page with links from other sites, low for everything else. On most sites a small group of pages earns most of the traffic. Those rows get the most attention.
How do you match old URLs to new ones?
Step 3: Match each URL to its new page
Match on content, not on URL similarity. The new page should answer the same question the old one did.
| Old page situation | new_path | status |
|---|---|---|
| Kept, same URL | Same path | 200 |
| Kept, new URL | Its new URL | 301 |
| Merged with another page | The merged page | 301 |
| Removed, close equivalent exists | The equivalent | 301 |
| Removed, relevant parent exists | The parent category | 301 |
| Removed, nothing relevant | Empty | 404 or 410 |
When a whole group of URLs moves the same way, one pattern rule beats hundreds of rows. For example, every dated blog URL moving to /blog/<slug>, in Apache:
RedirectMatch 301 ^/blog/\d{4}/\d{2}/(.+)$ /blog/$1
And in next.config.ts:
{ source: "/blog/:year(\\d{4})/:month(\\d{2})/:slug", destination: "/blog/:slug", permanent: true }
Write one row in the map for the rule itself, so the decision is recorded.
Step 4: Decide what happens to pages with no match
This is where traffic is lost. The tempting shortcut is to send every leftover URL to the homepage. Google treats redirects to an irrelevant page as soft 404s, so the old page's signals are dropped anyway, and the visitor lands somewhere that does not answer their question.
Send a removed page to a parent page only when the parent is genuinely related: a discontinued product to its category, an old job ad to the careers page. Otherwise let it return 404 or 410. Google's site move guide recommends exactly that for content that will not exist on the new site.
How do you turn the map into working redirects?
Step 5: Check for chains and loops
Sort the sheet and look for any new_path that also appears in old_path. That is a chain: /a goes to /b, which goes to /c. Point /a straight at /c. Do the same for redirects inherited from earlier redesigns. A loop, where two rows point at each other, will break the page entirely. How to fix redirect chains covers the clean-up on a live site.
Step 6: Turn the map into server rules
Filter the sheet to the 301 rows and copy the first two columns. The redirect generator accepts them pasted as comma- or tab-separated lines and writes rules for .htaccess, Nginx and next.config.ts, flagging duplicates and chains as it goes. On WordPress, the Redirection plugin can import a CSV of source and target URLs; check its documentation for the column order.
Two platform limits to know. As of October 2026, Vercel allows 1,024 redirects in a Next.js config; the Next.js redirecting guide suggests handling larger sets in Proxy (formerly Middleware). And a .htaccess file with thousands of lines is read on every request, so move large sets into the main server config or an Nginx map.
For which code to use, 301 vs 302 redirects has the full answer: 301 or 308 for anything permanent.
How do you test the map?
Step 7: Test every row after launch
Test on staging first, then again on the live site the hour it launches. This loop reads the CSV and prints where each old path ends up, the final status and the number of hops. Replace the domain with yours:
tail -n +2 redirect-map.csv | while IFS=, read -r old new status rest; do
[ "$status" = "301" ] || continue
printf '%s -> ' "$old"
curl -s -o /dev/null -L -w '%{url_effective} %{http_code} hops=%{num_redirects}\n' "https://www.example.com$old"
done
Each line should end with your expected new_path, a 200 and hops=1. Anything else is a broken row. Fill the tested column as you go, high-priority rows first.
Then keep watching. Over the following weeks, Search Console's Pages report shows any old URL you missed as "Not found (404)", and your server logs show it sooner. Add each one to the map and the server rules the day you find it. The week after a migration has the full monitoring routine, and how to migrate a website without losing SEO shows where the map fits in the move as a whole.
Was this guide useful?