Post-Deploy Verification Checklists

Most teams have a post-deploy checklist, even if it is only in someone's head: open the home page, click through to a guide, try search, check that the new page is there. It is done carefully after big releases and skipped after small ones, which is exactly when the unexpected breakage happens — a header rule lost in a host configuration change, a redirect file that failed to upload, a noindex from a preview build that leaked into production. The checklist is valuable; the manual execution is not.

This guide turns a static documentation site's post-deploy checklist into an automated suite that runs against production after every deploy: version, key pages, redirects, headers, assets, search, forms, indexing directives and a performance sanity check. It separates hard failures that roll back automatically from soft ones that alert a person. It is part of Rollbacks and Deploy Safety for Static Sites, and extends Running Smoke Tests Against a Preview URL to production.

Prerequisites

  • A production deploy step in CI, and a rollback command for the host (wrangler rollback, Netlify's API to restore a deploy, or a blue-green switch).
  • The build's version stamped into the HTML, as in Tracking Deploy Markers in Analytics.
  • Playwright and Node in CI.

Step 1: Write Down the Manual Checklist

The team's informal checklist, collected from three people who had done releases:

  1. The site loads and shows the new version.
  2. The home page, a guide, the API reference and the changelog load.
  3. Old URLs from the last migration still redirect.
  4. Security headers are present.
  5. Search returns results.
  6. The feedback form submits.
  7. The site is indexable (no stray noindex) and robots.txt is right.
  8. It "feels fast".

Each item became one or more automated checks, and two items that people had forgotten were added from past incidents: the sitemap is reachable and lists the new pages, and the 404 page returns a 404 status.

Mapping manual checklist items to automated post-deploy checks A matrix of checklist items. New version shown maps to comparing the version stamp with the commit, a hard check. Key pages load maps to 25 URLs returning 200 with expected headings, hard. Redirects map to 40 sampled redirect cases, hard. Security headers map to header assertions, hard. Search works maps to a Playwright query, hard. Feedback form maps to a dry-run submission, soft. Indexable maps to robots and noindex checks, hard. Feels fast maps to a Lighthouse run compared with the previous deploy, soft. From manual checklist to automated checks Manual item Automated check On failure New version live stamp matches SHA roll back Key pages load 25 URLs: 200 + heading roll back Redirects work 40 sampled cases roll back Security headers header assertions roll back Search works Playwright query roll back Indexable robots.txt + no noindex roll back Feels fast Lighthouse vs previous alert only
Hard checks roll back automatically; soft checks alert a person.

Step 2: Wait for the Deploy to Be Live

Checks that run before the new version is served test the old version and pass misleadingly. The suite first polls the version stamp until it matches the deployed commit, with a timeout:

async function waitForVersion(base, sha, timeoutMs = 180_000) {
  const short = sha.slice(0, 7), start = Date.now();
  while (Date.now() - start < timeoutMs) {
    const html = await (await fetch(`${base}/?v=${Date.now()}`, { headers: { 'Cache-Control': 'no-cache' } })).text();
    if (html.includes(`data-version="${short}"`)) return;
    await new Promise((r) => setTimeout(r, 5000));
  }
  throw new Error(`production still not serving ${short} after ${timeoutMs / 1000}s`);
}

The cache-busting query parameter and no-cache header avoid reading a stale edge copy; the check still requests the canonical URL for everything else. A timeout here is itself a hard failure: the deploy did not take effect.

Step 3: The Checks

The suite is a single Node script plus one Playwright test file, organized by the checklist:

  • Key pages: 25 URLs across templates, each must return 200, contain its expected h1, and reference the current version's stylesheet.
  • Assets: every script, stylesheet and preload referenced from those 25 pages returns 200 with the right content type.
  • Redirects: 40 cases sampled from the redirect map, including the oldest migration's URLs, via the table in Testing Redirects and Headers Before Deploy.
  • Headers: CSP, HSTS, X-Content-Type-Options, and cache headers on HTML and a hashed asset.
  • Indexing: robots.txt allows crawling and points at the sitemap; no page among the 25 carries noindex; the sitemap returns 200 and contains the URLs of pages added in this deploy.
  • 404 behaviour: a random missing URL returns status 404 with the 404 page.
  • Search: Playwright opens the search dialog, queries a known term and follows the first result.
  • Form (soft): submits the feedback form in dry-run mode.
  • Performance (soft): a single Lighthouse run of the home page and one guide, compared with the median of the last five deploys.

The whole suite ran in 74 seconds.

Time spent per group of post-deploy checks Horizontal bars of seconds per check group. Lighthouse sanity runs 31 seconds. Waiting for the new version 14 seconds on average. Search and form in Playwright 11 seconds. Key pages and assets 9 seconds. Redirects 5 seconds. Headers, indexing and 404 behaviour 4 seconds. Where the 74 seconds go Lighthouse sanity (soft) 31 s Wait for new version 14 s Search and form 11 s Key pages and assets 9 s Redirects 5 s Headers, indexing, 404 4 s
Median over 200 production deploys; groups run in parallel where independent.

Step 4: Roll Back on Hard Failures

Hard failures mean readers are affected now. The workflow rolls back without waiting for a person, then alerts:

- name: Post-deploy verification
  id: verify
  run: node scripts/verify-production.mjs --base https://docs.example.com --sha $GITHUB_SHA
- name: Roll back on failure
  if: failure() && steps.verify.outcome == 'failure'
  run: |
    npx wrangler rollback --message "auto-rollback: post-deploy verification failed for ${GITHUB_SHA::7}"
    node scripts/notify.mjs --level page --text "Production rolled back: $(cat verify-report.txt | head -5)"

After a rollback, the suite runs again against the restored version, to confirm the rollback worked and that the failure was caused by the deploy rather than by something external. Soft failures — a Lighthouse score 15% below the recent median, a form dry run failing — post to the team channel with the report attached. How fast the rollback itself is depends on the host, as covered in Rolling Back a Bad Static Deploy in Under a Minute.

Post-deploy verification and automatic rollback flow The deploy finishes. The suite waits until production serves the new version. Hard checks run on key pages, assets, redirects, headers, indexing, 404 behaviour and search. If any hard check fails, the host rolls back automatically, the suite reruns against the restored version and a person is paged. Soft checks for performance and forms only alert. After every production deploy Deploy done new version uploaded Wait for version stamp equals commit Hard checks pages, redirects, headers, search Fail → roll back rerun + page Soft checks perf, forms: alert only
Rollback does not wait for a human when the failure is unambiguous.

What It Caught

In the first year, across about 1,100 production deploys, the hard checks triggered six automatic rollbacks:

  • A host configuration change that dropped the _headers file from the deploy: CSP and HSTS missing.
  • A preview-only noindex header rule that was accidentally applied to production.
  • A redirect file that exceeded the host's rule limit and was silently truncated; 31 old URLs returned 404.
  • Two builds where a template error produced an empty <main> on the API reference, which the pre-deploy snapshot test had not covered for that template.
  • A search index that failed to upload because of a file-size limit, leaving the new HTML pointing at missing index fragments.

Each would previously have been found by a reader, or by search traffic dropping. Soft checks flagged 14 performance regressions, of which 11 were real.

Keep the checklist and the suite in step: when an incident reveals a failure mode no check covered, the fix includes a new check, and the incident review records which check it is. Half of the current checks on this site were added that way.

Measured Impact

MeasureManual checklistAutomated verification
Deploys verified~20% (big releases)100%
Incidents reaching readers from bad deploys (per year)70
Median time from bad deploy to rollback2 h 10 min2 min 40 s
Automatic rollbacks—6
Person-minutes per deploy~10 (when done)0

Pitfalls & Rollback

  • Checking before the new version is live. Wait for the version stamp or the checks test the old site.
  • Rolling back on soft signals. Lighthouse variance would cause needless rollbacks; alert instead.
  • Checks that depend on third parties. Keep external services out of hard checks, or a vendor outage rolls back a good deploy.
  • No re-check after rollback. Confirm the restored version passes, or you may be chasing an external failure.
  • Rollback of the automation: the verification step can be switched to report-only by removing the rollback step.

Conclusion

A post-deploy checklist is only useful if it runs every time, and it only runs every time if a machine runs it. Turn each manual item into a check against the real production hostname, wait for the new version before checking, separate hard failures that roll back automatically from soft ones that alert, and re-verify after rolling back. On this site that verified every one of 1,100 deploys, rolled back six bad ones within minutes, and ended reader-facing incidents from deploys.

FAQ

What should be checked after deploying a static site?

That production serves the new version, that key pages load with the right content, that redirects and security headers are intact, that assets referenced by the HTML exist, that search and any forms work, that robots and indexing directives are correct, and that performance has not regressed sharply.

How is post-deploy verification different from pre-deploy tests?

Pre-deploy tests check the build and a preview. Post-deploy checks run against the real production hostname, through the real CDN configuration, custom domain, certificate and caching, which previews do not always share. They catch configuration and environment differences.

Should failures trigger an automatic rollback?

For checks that are deterministic and clearly mean the site is broken, such as the home page returning an error or the version not updating, yes. For softer signals, such as a slower Lighthouse score, alert a person instead of rolling back automatically.

How long should the checks take?

Short enough to run on every deploy without delaying the next one, typically one to three minutes. The suite here ran in 74 seconds, with slower checks such as a full link crawl scheduled separately.

Filed underHow-tos