Rolling Back a Bad Static Deploy in Under a Minute

Rolling back a static site is genuinely fast — the previous build already exists, complete and immutable, at a URL your host can promote. What makes real rollbacks slow is everything around the command: establishing what is live, finding the right deployment ID, remembering the syntax, and stopping the pipeline from redeploying the broken build while you work.

This is the runbook. It assumes the safety practices in Rollbacks and Deploy Safety for Static Sites are in place — atomic deploys, retained artifacts, a build stamp in the HTML.

Prerequisites

  • A host that keeps previous deployments addressable: Cloudflare Pages or Workers, Netlify, Vercel, or your own versioned upload scheme.
  • Deploy credentials available to whoever is on call, not only to CI.
  • A build stamp in the output (<meta name="build" content="a91f3c2">) so you can identify the live version from a browser.

The Sequence

Rollback sequence with timings Five steps with elapsed times: confirm the symptom and read the build stamp at ten seconds, pause the deployment pipeline at fifteen seconds, list deployments and identify the last good one at twenty-five seconds, promote it at forty seconds, and verify plus communicate at fifty-five seconds. Under a minute, if nothing has to be decided on the spot 1 · Confirm read build stamp 0:10 2 · Pause CI no redeploys 0:15 3 · Identify last good ID 0:25 4 · Promote pointer flip 0:40 5 · Verify + tell people 0:55 What is NOT in the critical path diagnosing the cause writing or reviewing a fix rebuilding anything waiting for CI Every item in the lower boxes is what turns a one-minute recovery into a thirty-minute one
The times are achievable because nothing in the sequence produces anything new — it selects an artifact that already exists and points production at it.

1. Confirm what is live (10 s)

curl -s https://example.com/ | grep -o '<meta name="build"[^>]*>'
# <meta name="build" content="a91f3c2" data-built="2026-08-01T09:14:22Z">

Two facts matter: which build is serving, and whether the symptom started when it went out. If the symptom predates this deployment, rolling back will not fix it and you have wasted the fastest minute you had.

2. Stop the pipeline (5 s)

This step is skipped more often than any other, and it is why some incidents recur mid-rollback. If the default branch still contains the bad commit, any retried workflow or subsequent push republishes it.

gh workflow disable deploy.yml          # GitHub Actions
# or revert first, which also fixes the branch:
git revert --no-edit <bad-sha> && git push

3. Find the last good deployment (10 s)

# Cloudflare Workers / Pages
npx wrangler deployments list
# Netlify
netlify api listSiteDeploys --data '{"site_id":"'"$SITE_ID"'"}' \
  | jq -r '.[0:5][] | "\(.id)  \(.created_at)  \(.commit_ref[0:7])  \(.state)"'
# Vercel
npx vercel ls --prod --json | jq -r '.deployments[0:5][] | "\(.uid)  \(.created)  \(.meta.githubCommitSha[0:7])"'

Match the commit column against your build stamp: the entry immediately before the current one is normally the target.

4. Promote it (15 s)

# Cloudflare
npx wrangler rollback <deployment-id> --message "incident: nav partial missing"
# Netlify
netlify api restoreSiteDeploy --data '{"site_id":"'"$SITE_ID"'","deploy_id":"'"$DEPLOY_ID"'"}'
# Vercel
npx vercel promote <deployment-url>
# Own infrastructure (symlink swap)
ssh deploy@origin "ln -sfn /var/www/releases/$PREV /var/www/current.new && mv -Tf /var/www/current.new /var/www/current"

5. Verify and say so (15 s)

node scripts/smoke.mjs https://example.com && \
  curl -s https://example.com/ | grep -o '<meta name="build"[^>]*>'

Then post one message: what broke, that production is restored to build X, and that the cause is being investigated. The message costs ten seconds and prevents three people from independently starting their own rollback.

Write the Runbook Where People Look

The commands above are useless at 2 a.m. if they live in someone's shell history. Put a RUNBOOK.md at the repository root with the exact commands for your host, the site or account identifiers filled in, and the location of credentials. Link it from the deploy workflow's summary output so the person watching a failed deploy sees it without searching.

Three details make a runbook usable under pressure. Write commands to be copy-pasteable with no placeholders — put the real site ID in, since a runbook is not a secret and the credentials are separate. Include the expected output of each command, so a responder knows whether it worked without interpreting. And state the decision rule at the top: "if the homepage or any template page is broken for readers, roll back first and diagnose after" removes the hesitation that costs the most time.

Anatomy of a usable rollback runbook A runbook page divided into four blocks: a decision rule at the top, the exact commands with real identifiers, expected output for each command, and where credentials live. A note marks that anything requiring interpretation belongs in the post-incident review instead. Four blocks, in this order 1 · Decision rule — when to roll back without asking anyone 2 · Commands — real identifiers, no placeholders to fill in 3 · Expected output — so success is recognisable, not inferred 4 · Credentials — where they are, who has them, how to get them
Anything that requires judgement belongs in the post-incident review, not in the runbook. The runbook exists to remove decisions from the minute when decisions are most expensive.

Caches: Usually Leave Them Alone

The instinct during an incident is to purge everything. On a static site that is usually wrong and occasionally harmful.

Asset typeTypical policyAction on rollback
HTMLshort TTL or revalidatedNothing; next revalidation serves the restored build
Hashed JS/CSSimmutable, one yearNothing; old filenames were never overwritten
Unhashed assets (/logo.svg)medium TTLPurge these specific paths if they changed
_redirects / headerspart of the deploymentRestored with the deployment on most hosts

A full purge sends every edge location to the origin at once, which is a self-inflicted traffic spike at the worst possible moment. Purge paths, not everything — the policy background is in CDN Caching Rules for SSGs and Purging the CDN Cache After a Static Deploy.

After the Rollback

The incident is over for readers; it is not over for you. Three things follow, in order.

Keep the broken build addressable. Do not delete or overwrite it. It is the evidence, and reproducing the failure from the artifact is far easier than reproducing it from a description. Note its deployment ID in the incident record.

Diagnose against the artifact, not production. Deploy the bad build to a preview URL and debug there. This removes all time pressure and means the investigation cannot accidentally affect readers.

Close the loop with a check. Almost every rollback points at a missing gate. A dropped navigation partial should have been caught by a smoke-test marker; a broken redirect by the redirect verifier; a schema violation by the build. Adding that one check is what stops the same class of failure recurring, and it is the only part of the process that compounds.

Resist the urge to redeploy the fixed version immediately after the rollback. The site is currently serving a build known to be good, which is a fine place to be for an hour. Ship the fix through the normal pipeline, with review and previews, rather than through the emergency path that just failed.

Measured Impact

Timings from four rehearsals and two real incidents on a documentation site hosted on Cloudflare Workers, recorded from the moment the responder started acting:

StepRehearsed teamTeam without a runbook
Confirm what is live10 s2–4 min (no build stamp)
Pause the pipeline5 soften skipped
Identify last good deployment10 s3–6 min (finding credentials, syntax)
Promote15 s1–2 min
Verify15 sad hoc, by eye
Total to restored site55 s12–20 min
Recovery time with and without a rehearsed runbook A horizontal bar chart on a shared scale. The rehearsed team restores the site in 55 seconds. The team without a runbook takes between 12 and 20 minutes, with the largest components being identifying what is live and finding the right deployment. Same host, same command — different preparation Rehearsed runbook + build stamp 55 s No runbook same tooling available 12–20 min Most of the gap is not the rollback command — it is establishing what is live and locating credentials Both figures exclude detection time, which was around four minutes in every case
The tooling is identical in both rows. The difference is a stamped build, a written command and someone who has run it before.

Pitfalls & Rollback

  • Rolling back before checking the symptom's start time. If the fault predates the current deployment, you have restored a build that also has it.
  • Forgetting to stop the pipeline. A retried job or a colleague's push re-promotes the broken build mid-incident.
  • Purging the entire cache. It converts a recovered site into an origin traffic spike. Purge specific paths only.
  • Rolling back configuration you deploy separately. Redirect rules or headers managed outside the artifact need their own revert.
  • No credentials off CI. If only the pipeline can deploy, only the pipeline can roll back — and the pipeline is often the thing you just paused.
  • Rollback of the rollback: if the restored build is also wrong, go back one more deployment. This is why retention of thirty days matters more than retention of one.

Conclusion

A static rollback is a pointer change, so the recovery time is decided entirely by preparation: a build stamp so you know what is live, credentials in human hands, a written command per host, and the discipline to pause the pipeline first. Rehearse it once a quarter and the real incident becomes unremarkable. The surrounding practices — atomic deploys, artifact retention, smoke tests — are in Rollbacks and Deploy Safety for Static Sites.

FAQ

Should I roll back or fix forward?

Roll back first if readers are seeing something broken, then diagnose without time pressure. Fix forward only when the fault is trivially understood and the fix is a one-line change you can ship faster than a rollback, which is rare once you have measured both.

How do I know which deployment was the last good one?

Every host lists deployments with a timestamp and commit. The last good one is the deployment that was live before the current one, unless the problem predates it — which is why a build stamp in the HTML and a quick check of when the symptom started are the first two steps.

Does rolling back require purging the CDN cache?

Usually not, and purging everything is often harmful. HTML is typically short-cached or revalidated, and hashed assets are immutable so the old build's assets are still valid. Purge only the specific HTML paths if your cache policy holds them long.

What if the bad deploy also changed my redirect rules or headers?

On hosts where those files are part of the deployment artifact, rolling back the deployment restores them too. Where they are configured outside the build, revert them separately, and note that in the runbook so nobody assumes one action covered both.

How do I stop the pipeline from immediately redeploying the bad build?

Revert the offending commit on the default branch or pause the deployment integration before rolling back. Otherwise the next push, or a retried workflow, promotes the broken build again while you are still investigating.