A static host shipped the project instead of the site.
A T-Minus Labs postmortem describes a 150 KB engineering handoff that returned HTTP 200 from a production domain. The file held an admin bypass string, an unfixed payment flaw, and internal database names. The build output directory was the repository root, so tracked notes and tool configuration became public files.[1]
The author removed the files and redeployed. A separate cache layer still served the old copy on the custom domain. Cache-busted requests reached the clean origin, but visitors using the plain URL could still fetch the cached file. The repair needed two parts: block the exposed paths at the edge, then fix the server-side payment flaw that made the leaked information useful.[1]
The release boundary is a directory, not an intention.
Make the public tree boring.
Do not publish a mixed project directory and hope a denylist catches every private file. Put the deployable site in a dedicated directory such as public/ or dist/. Generate that directory from known inputs. Keep notes, prompts, CI files, editor state, backups, credentials, and repository metadata outside it.
Then inspect the exact transfer list before promotion. Rsync can list one source without copying it. It also supports --exclude, --exclude-from, and --files-from. Those controls define what moves, but they do not repair a source directory that mixes public and private material.[2]
rsync -anv --itemize-changes \
--exclude='.git/' --exclude='.gitignore' \
public/ host:/var/www/example/
Do not make the web server guess.
Caddy's file server joins the request path to the configured site root. It can hide file names and patterns, but its documentation says hide should not be treated as a security boundary for sensitive paths. The same page warns that symbolic links inside the root can reach outside it.[3]
Use the smallest root that can serve the site. Keep server-side hides for defense in depth. Test metadata paths after every deployment. A production check should request /.git/HEAD, /.git/config, internal note names, source maps when they are not intended, and backup suffixes.
Verify the visitor's route.
A cache-busting query answers whether the origin is clean. It does not answer whether the ordinary URL is still exposed. Check both when diagnosing. Close the incident only after the plain URL fails, the origin route fails, and the sensitive mechanism has been removed or made harmless.
HTTP 404 is one witness. Add filesystem inventory, transfer hashes, and a clean deploy source. If an internal file ever reached the public tree, assume copies may remain. Rotate real credentials, remove client-side trust, and fix the underlying authorization flaw.