Skip to content
AgencyProofby Intelliblitz

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.

Balaji Ponnambalam9 min read
A switching yard from a low bridge: a mole leans back on one signal lever while one rail runs clean into a lit arch, another loops through extra points to reach it, and a third stops at a bricked-up arch.

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 URLStatusDestinationHopsQuery and fragmentCanonical on destinationDecision
/services/root-canal301/treatments/endodontics2Query dropped at hop 1Selfredirect
/offers/spring?utm_source=news404none0n/an/aneeds an owner
/team/j-okafor200itself0n/aSelfkeep as is
/careers/2019-intern-scheme301/1n/aHomeretire on purpose
/blog/feed.xml302/feed1n/an/aredirect
Scroll for more →

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.

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"
Redirect chain, fixture host
301 location: https://www.oldsite.example/services/root-canal 302 location: /treatments/endodontics 200 OK

EstablishesTwo 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.

Some rows in the ledger cannot be fixed by you at all, and pretending otherwise is how they get skipped.

Where the link livesWho controls itCan it be updatedWhat happens if it is not
Email footersThe client's marketing teamYes, on the next sendOld link keeps arriving for years
Printed materialNobody, once printedNoRedirect is the only remedy
Ad platformsWhoever holds the accountYes, per campaignSpend lands on a redirect or a 404
Partner sitesA person at another companyOnly by askingLink rots, and so does the referral
Search resultsThe search engineIndirectly, over timeOld result shown until it recrawls
BookmarksIndividual peopleNoOne person meets a dead end, silently
Scroll for more →

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 confirmation

Nobody 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-scheme is 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.