Manual
The redirect map a redesign needs before the new site goes live
Old URLs do not disappear when a site is replaced. They sit in email footers, printed material, ad platforms and other people's links. This is the inventory that decides which ones get a destination, which get retired on purpose, and which need an owner.

Somebody printed ten thousand appointment cards with a URL on the back. They are sitting in a waiting room, and there is no deploy that reaches them.
That is the shape of the problem a redesign creates. The new site is a thing you authored. The URLs pointing at the old one are an installed base you did not author, cannot recall, and mostly cannot see: email footers, ad platforms, partner pages, printed material, other people's links, and one bookmark that happens to belong to the finance director.
So the work is an inventory, built before the switch rather than after the
first complaint, and every row in it ends in one of four decisions:
redirect, keep as is, retire on purpose, or needs an owner. Three of
those are answers. The fourth is the only honest thing to write when nobody in
the room knows who controls the link.
| Old URL | Status | Destination | Hops | Query and fragment | Canonical on destination | Decision |
|---|---|---|---|---|---|---|
/services/root-canal | 301 | /treatments/endodontics | 2 | Query dropped at hop 1 | Self | redirect |
/offers/spring?utm_source=news | 404 | none | 0 | n/a | n/a | needs an owner |
/team/j-okafor | 200 | itself | 0 | n/a | Self | keep as is |
/careers/2019-intern-scheme | 301 | / | 1 | n/a | Home | retire on purpose |
/blog/feed.xml | 302 | /feed | 1 | n/a | n/a | redirect |
That is the redirect verification ledger. The first six columns record what is
observed today; the last one records what should happen, which is why row four
can read 301 to / and still be decided as retire on purpose. It is worth
more filled in badly than left empty, and rows two and four matter most,
because neither is about the new site at all.
AgencyProof observes redirect hops on the pages it samples, and reports them as a performance signal: "Checks first-pass performance signals on sampled pages." That is not this inventory. A scanner does not know which URLs were printed, which ones a campaign bought, or which ones a partner published. AgencyProof inspects a bounded sample of reachable public pages. The report records the pages and methods used, plus important checks that were not performed. The inventory is yours to build.
Campaign links, and what they are carrying
A campaign link is not a path. It is a path plus a query string that somebody is measuring, and the two are separated by the first careless redirect. Run the chain and watch:
$ curl -sIL 'https://oldsite.example/services/root-canal?utm_source=news'
HTTP/2 301
location: https://www.oldsite.example/services/root-canal
HTTP/2 302
location: /treatments/endodontics
HTTP/2 200
link: <https://www.oldsite.example/treatments/endodontics>; rel="canonical"
301 location: https://www.oldsite.example/services/root-canal
302 location: /treatments/endodontics
200 OKEstablishesTwo redirects stand between the campaign link and the page, and the query string does not survive the first.
Does not establishWhether that page is the right destination for the campaign, or whether anyone is still following the link.
Three separate defects sit in six lines. The first hop drops the query, so every arrival from that campaign is now attributed to nothing. The second hop is a 302, which announces that the move is temporary when it is not. And there are two hops where one would do.
Collapsed, it looks like this:
$ curl -sIL 'https://oldsite.example/services/root-canal?utm_source=news'
HTTP/2 301
location: https://www.oldsite.example/treatments/endodontics?utm_source=news
HTTP/2 200
link: <https://www.oldsite.example/treatments/endodontics>; rel="canonical"
One hop. Query preserved. Canonical declared without the query, which is a separate statement and a deliberate one: RFC 6596 defines the canonical link relation as specifying "the preferred IRI from resources with duplicative content", and requires that the target it names "MUST identify content that is either duplicative or a superset of the content at the context (referring) IRI". The redirect says where to go. The canonical says which address that content should be filed under. Neither implies the other.
Parameters the destination does not read
Preserving a query string is not the same as honouring it. If the old page read
?ref= and the new template ignores it, the parameter now travels intact to a
page that does nothing with it, and the report that was built on it quietly
flattens. Write the parameter names into the ledger row, not just the word
"query".
Old service pages, where the mapping is not one to one
The clean case is a rename. The common case is a restructure, and a restructure does not produce a mapping you can generate.
One old page, two new pages
The old /services/root-canal became a treatment page and a pricing page.
Something has to pick. The rule that survives a client conversation is to send
the old URL wherever the old page's traffic was trying to get to, then link
prominently to the other, and record which choice was made and why. A guess
that is written down can be revisited. A guess that is not becomes folklore.
Two old pages, one new page
Both old URLs redirect to the same destination and both are correct. What needs saying out loud is that the two audiences merged, because somebody will eventually ask why a page that used to convert differently now reports as one number.
Links nobody at your agency controls
Some rows in the ledger cannot be fixed by you at all, and pretending otherwise is how they get skipped.
| Where the link lives | Who controls it | Can it be updated | What happens if it is not |
|---|---|---|---|
| Email footers | The client's marketing team | Yes, on the next send | Old link keeps arriving for years |
| Printed material | Nobody, once printed | No | Redirect is the only remedy |
| Ad platforms | Whoever holds the account | Yes, per campaign | Spend lands on a redirect or a 404 |
| Partner sites | A person at another company | Only by asking | Link rots, and so does the referral |
| Search results | The search engine | Indirectly, over time | Old result shown until it recrawls |
| Bookmarks | Individual people | No | One person meets a dead end, silently |
The third column is the one that changes behaviour. Anything you cannot update must be redirected, permanently, and that redirect has to outlive the project. Anything you can update should still be redirected, because the update will be incomplete.
Printed material
Printed URLs are the strongest argument for keeping a redirect alive after everyone has forgotten why it exists. There is no expiry on a card in a drawer. When the ledger row for a printed URL has no owner, that is not an administrative gap, it is a finding:
Needs confirmationNobody at the agency can resolve it from public evidence. Somebody at the client has to say which URL was printed, on what, and how many are out there.
Bookmarks, typed paths and one person's muscle memory
The finance director has oldsite.example/invoices bookmarked and will not
mention that it stopped working. They will assume it moved and wait to be told.
You cannot enumerate bookmarks. What you can do is take the old site's most requested paths from its access logs, add the paths that appear in internal documents, and treat that as the bookmark list. It is not complete. It is better than the alternative, which is nothing.
Machine entry points
Feeds, sitemaps, robots.txt, /.well-known/ paths and any endpoint another
system calls are all URLs, and they behave differently from pages under
redirection.
Endpoints that do not follow redirects
Our own host configuration carries this lesson as a comment. AgencyProof
canonicalises its raw deployment alias to the apex with a 301, and deliberately
excludes /api/* from that rule, because payment and email webhooks POST to
whatever URL is registered with them and do not follow 3xx: a redirect reads to
the sender as a failed delivery. The webhook keeps serving on the old host
until its registered endpoint is repointed by hand.
The same file records a second lesson, and it is the one that catches teams
midway through a migration. The apex, the www host and a retired brand
subdomain are all canonicalised by the platform at the edge. Adding them to the
application's own redirect rule fought that and produced a loop between apex
and www. As MDN puts it, "Redirection loops happen when additional
redirections follow the one
that has already been followed", and a loop spread across two systems is
exactly the kind neither one can detect on its own. Two components each doing
half a redirect is the most common way a redesign takes a site off the air.
Method matters here too. RFC 9110 §15.4 permits a user agent to change the request method from POST to GET after a 301 or a 302, and forbids it after a 307 or a 308. If anything POSTs to a URL you are moving, 308 is the code that preserves the request, and 301 is the code that can quietly turn it into a GET with no body.
Pages you are removing on purpose
The class everyone skips. A page is being deleted, nothing replaces it, and no decision gets written, so it becomes a 404 by default and joins the list of things that look like defects for the next three years.
Retiring a URL is a real decision with a real status code. RFC 9110 §15.5.11 defines 410 Gone for a resource that is no longer available at the origin server where that condition is likely to be permanent. Where you do not know whether the removal is permanent, 404 is the correct answer instead. The difference is not cosmetic: 410 states an intention, 404 states an absence, and only one of them tells the next person that somebody decided.
The reason this matters commercially is that a redirect is a signal, not an instruction. Google's own documentation describes a permanent redirect this way: "the indexing pipeline uses the redirect as a signal that the redirect target should be canonical", and says a temporary redirect does not carry that signal. Redirecting a retired page to the home page therefore argues, weakly and repeatedly, that the home page is that content. It is not.
IntentionalThe behaviour is a control working as designed.That is the marker the ledger row deserves, and it is only truthful once somebody has written the line. Here is the line:
/careers/2019-intern-schemeis retired on purpose. It returns 410, it is out of the sitemap, and Priya confirmed on 12 August that no current campaign or printed material points at it. Review at the next site check.
A URL, a status, a named person, a date, and a review point. Write that for the class you were going to skip, and the 404 somebody finds next spring stops being an accusation and becomes a record.
Provenance
- Written by
- Balaji PonnambalamFounder, AgencyProof
- Technical review
- Written and self-reviewed against the codebaseAgencyProof is a one-person engineering team; there is no second reviewer to claim.
- Published
- Evidence basis
- Examples in this article are synthetic fixtures built for the explanation.
Every product claim in an article is checked against the AgencyProof source tree before publication, and traced to the code path that implements it. Where the article describes a method rather than a shipped behaviour, it says which it is.
Related reading
Audit LabHow to tell a broken link from a protected endpoint
A response code is evidence, not a verdict. How to read 404, 403, 429, timeouts and bot challenges before calling a link broken.
Audit LabWhen your structured data and your page copy describe different companies
Structured data cannot repair an unclear public identity. Compare the organization block against the page copy first.
Agency Field NotesWhat to check on a live site when nothing is supposed to have changed
Recurring QA earns its place by catching drift, not by repeating the launch ceremony on a site nobody changed.