Static publishing removes application runtime complexity, but it does not remove the boundary between a generated directory and a public response. A release check should cover both.
Start from a clean artifact
Build with the pinned generator into a clean destination. Record the source commit, generator version, output file list, and checksums of files that users download. A clean build catches stale files that a copy-over deployment can accidentally preserve.
set -eu
make verify
find public -type f -print | sort
The file list is evidence, not decoration. If a route disappears, the diff should explain whether the source was removed or the generator failed to emit it.
Exercise three response classes
Before publishing, test the staged artifact with the intended server configuration. After switching directories, repeat these checks at the public edge; probing the old live site cannot validate the new release. Request one HTML page, one static resource, and one missing path. Inspect status, content type, cache policy, and security headers:
set -eu
curl --compressed -fsS -D page.headers -o page.body https://www.example.test/
curl --compressed -fsS -D asset.headers -o asset.body \
https://www.example.test/downloads/check-http-headers.sh
curl --compressed -sS -D missing.headers -o missing.body \
https://www.example.test/does-not-exist
cmp public/index.html page.body
cmp public/downloads/check-http-headers.sh asset.body
cmp public/404.html missing.body
Run each fragment as a standalone shell script or in a disposable shell session. set -eu stops this sequential script after a failed build, transfer, or comparison, so a later successful command cannot hide an earlier failure. The missing-path request intentionally does not use -f: a 404 is expected and should still be captured. Check the final status in each header file: 200, 200, then 404; the byte comparisons do not automate that status check. No redirect is followed here. --compressed decodes supported content codings before comparison; a deliberate server-side HTML transformation needs its own documented comparison rule. cmp is silent on equality and returns nonzero on a difference. Repeat for /zh/ and its error page rather than assuming language parity.
Keep rollback simple
Do not delete the previous directory until the new one has passed the checks. A rollback should select a known-good directory, not reconstruct one from memory during an incident. The release checklist and HTTP header check are deliberately small enough to run from a clean shell.
Sources
- RFC 9110 — HTTP Semantics, status and representation semantics, checked 2026-07-09.
- curl manual, response inspection options, checked 2026-07-09.
- Static-site release checklist, local companion resource.