Rolling Back a Broken Redirect Map Safely
Problem Statement
A redirect deployment has gone wrong: a regex is greedier than intended, a mapping file was generated from the wrong column, or a rule ordering change has sent a whole template into a loop. Reverting sounds simple until you notice that the previous rules are also wrong now — the origin has moved, some paths only exist on the new stack, and a straight revert would strand the traffic that has already migrated correctly. A redirect rollback is a surgical operation on a subset of rules, not a git revert. This page sits under Redirect Rollback & Recovery.
When to Use This Approach
- A redirect deploy has produced loops, wrong destinations, or a spike in 404s attributable to one rule group.
- Part of the map is demonstrably working and you do not want to undo it.
- The fault is in the rules themselves rather than in the origin or DNS — a DNS-level fault is handled by Reverting DNS Records During a Failed Cutover instead.
- You have the previous rule set in version control and can diff it segment by segment.
Why “Just Revert It” Is the Wrong Instinct
Reverting a deployment is a well-drilled reflex, and it is correct for application code because the previous version was a working whole. A redirect map deployed during a migration is not a working whole — it is the current step in a sequence, and the step before it assumed an origin, a routing layer or a URL structure that has since changed. The previous version is not “known good”; it is “known good under conditions that no longer hold”.
There is a second difference. Application rollbacks are usually all-or-nothing because the code is coupled. Redirect rules are not coupled: the rule for /p/ has no relationship to the rule for /blog/ beyond sharing a file. That independence is what makes a partial revert both possible and correct — and it is thrown away by any rule set organised as one undifferentiated block.
The third difference is the one that surprises people mid-incident. A redirect is cached in more places than a page, including in browsers that will honour a permanent redirect for as long as its cache lifetime says. A page rollback is visible to the next request; a redirect rollback is visible only after every cache in the path has been convinced, and one of them cannot be reached at all.
# Check what you are committing to before you deploy a permanent redirect
curl -sI https://example.com/p/xz-400 | grep -i 'cache-control\|^HTTP'
Those three differences produce the approach in this page: establish scope first, revert the narrowest thing that fixes the fault, purge outwards, and confirm you did not undo work that was fine.
Step-by-Step Instructions
1. Establish the Blast Radius Before Touching Anything
Identify which paths are affected and confirm the boundary. A rollback aimed at the wrong segment does damage in two places at once.
# Bucket the last hour of non-200 responses by first path component
awk '$9 !~ /^(200|301)$/ {split($7,a,"/"); print a[2], $9}' /var/log/nginx/access.log \
| sort | uniq -c | sort -rn | head -20
2. Freeze Further Deploys
A rollback racing a hotfix produces a rule set nobody can reason about. Announce the freeze in the same channel the go-live runbook uses, then proceed.
3. Revert the Faulty Segment Only
Keep the rest of the file as deployed. Where rules live in an included file per template, this is a one-file revert; where they live in one monolith, extract the segment first — this is the moment the structure pays for itself.
# Revert one rule file to the last known-good commit, leaving the others alone
git checkout "$LAST_GOOD_SHA" -- conf/redirects/products.map
git diff --stat HEAD -- conf/redirects/ # confirm exactly one file changed
# Reload rather than restart: in-flight connections are not dropped
nginx -t && nginx -s reload
4. Invalidate Caches in the Right Order
A redirect is cached in more places than a page: the CDN, any reverse proxy, and — for a 301 — the browser. Invalidate from the origin outwards, or a purged edge will immediately re-cache the stale rule from a layer you have not cleared yet.
# Origin first, then edge, then confirm; purging in the other order re-poisons the edge
curl -sX POST "$PROXY_ADMIN/purge?path=/p/*" -H "Authorization: Bearer $PROXY_TOKEN"
curl -sX POST "https://api.cloudflare.com/client/v4/zones/$ZONE/purge_cache" \
-H "Authorization: Bearer $CF_TOKEN" -H 'Content-Type: application/json' \
--data '{"prefixes":["example.com/p/"]}'
5. Re-run the Map Validation Suite
Rollback is not complete until the same suite that should have caught the fault passes against the live site — the checks described in Validating Redirect Maps Before Deployment.
Worked Example
An electronics retailer deployed a regex consolidating three legacy product URL shapes into one. The rule was:
# The fault: .* also matches the destination, so the rule re-matches its own output
rewrite ^/product/(.*)$ /p/$1 permanent;
Because /p/ was itself served by a route that internally rewrote to /product/, every product URL entered a loop and returned a browser error after twenty hops. Categories, articles and the catch-all were unaffected.
The rollback ran as follows:
- Log bucketing confirmed 100% of the errors were under
/product/and/p/, and nothing else was affected — blast radius established in four minutes. - Only
conf/redirects/products.mapwas reverted; the other three rule files stayed on the new deployment. - Nginx was reloaded, then the proxy and CDN were purged for the
/p/prefix in that order. - Loop count was re-measured with
curl -sIL --max-redirs 5, and the affected sample returned single-hop 301s. - The corrected rule — anchored so it could not match its own output — went out the following morning through the normal release process rather than as a hotfix.
Total time to recovery was 26 minutes, of which 18 were cache propagation.
Verification
# 1. No loops: a bounded follow should complete, not exhaust the redirect limit
curl -sIL --max-redirs 5 https://example.com/product/xz-400 | grep -c '^HTTP' # expect <= 2
# 2. The reverted segment behaves as it did before the bad deploy
while read -r u; do
printf '%s %s\n' "$(curl -s -o /dev/null -w '%{http_code}' -I "$u")" "$u"
done < products-sample.txt | sort | uniq -c
# 3. Untouched segments are still on the new rules — confirm you did not over-revert
curl -sI https://example.com/c/laptops | awk '/^HTTP|^[Ll]ocation/'
Check three matters as much as check one. The most common secondary incident during a redirect rollback is reverting more than intended and quietly undoing work that was fine, which does not show up in error rates — it shows up weeks later as traffic that never moved.
FAQ
Should I revert the whole rule file or just the faulty rules? Just the faulty segment, provided your rules are grouped so that a segment can be reverted in isolation. If they are not, the rollback is a good moment to split them — a monolithic rule file forces every incident to be an all-or-nothing decision.
Can I roll back a 301 that browsers have already cached?
Not directly. A permanent redirect cached by a browser persists until its max-age expires or the user clears it. The mitigation is preventative: serve redirects with a short cache lifetime during a migration window and lengthen it once the map has been stable for a few weeks.
Is it safer to switch the bad rules to 302 rather than remove them? Sometimes, yes. If the destination is right but temporarily broken, downgrading to a temporary redirect stops the mapping being treated as permanent while you fix the destination. If the destination itself is wrong, remove the rule — a wrong 302 is still wrong.
How do I stop the same fault reaching production again? Run the redirect map through an automated validation suite in the pipeline: no loops, no chains, every destination 200, no rule matching its own output. Every fault in this page’s example would have been caught by the last of those four assertions.
What if the rollback makes things worse? Then the previous state was not actually good, which is common mid-migration — the old rules assume an origin that has moved. That is the point at which the decision escalates from a rule rollback to the DNS-level revert covered in the rollback procedures for the cutover itself.
Should I reload or restart the server? Reload. A reload applies the new configuration to fresh connections while in-flight requests finish on the old one; a restart drops them. During an incident the difference is a few hundred users who see a connection error on top of the fault you are already fixing.
How do I know the purge actually worked?
Request the URL through the edge and read the cache-status header, then request the origin directly with a Host header and compare. If the edge still returns the old destination while the origin returns the new one, the purge did not take effect and re-running it is more useful than waiting.
Is it safe to roll back while a Change of Address filing is active? Yes, provided the legacy host keeps redirecting to the new one. What breaks the filing is the legacy host ceasing to redirect, not the internal rules on the destination changing. A rollback that restores a working single-hop redirect is neutral to the filing.
Who should be allowed to call a redirect rollback? The on-call engineer, without escalation, against a threshold agreed in advance. Redirect faults compound quickly and the revert is narrow and reversible, so requiring a decision meeting costs more than it protects. Wider reverts — DNS, whole cutover — are the ones that warrant a second signature.
Should the bad rules be deleted or kept as a commented block? Delete them and rely on version control. A commented-out block in a live configuration is read as intent by the next person to touch the file, and during an incident somebody eventually uncomments it to “see if it helps”.
Related
← Back to Redirect Rollback & Recovery