On 11 June 2026 at 20:53 UTC+2, snakken.app started answering every request with a 301 redirect — to snakken.app. Browsers showed ERR_TOO_MANY_REDIRECTS; the site was effectively down for roughly six minutes. Nobody's data was at risk at any point. This is the write-up, because we said we'd publish the post-mortems and a small one is the right place to start the habit.
Timeline
- 20:41 — A change deploys adding a canonical-host redirect:
www.snakken.appshould301to the apex domain. SEO hygiene, one new nginxserverblock. - 20:53 — The rollout completes. Spot-checking the result shows the apex domain itself returning
301 → https://snakken.app/— a loop. - 20:55 — Root cause identified, fix committed.
- 20:58 — Fix deploys through the normal pipeline. Apex serves
200, www serves the intended301.
Detection was luck-adjacent: we were verifying the deploy as it landed. There was no alert; more on that below.
Root cause
nginx selects which server block handles a request by matching the Host header against server_name — and when nothing matches, it falls back to the default server, which (unless marked explicitly) is simply the first block in the file.
The original config had one server block, so it was trivially the default. The change added the redirect block above it:
server {
server_name www.snakken.app;
return 301 https://snakken.app$request_uri;
}
server {
server_name _; # the static site — no longer the default!
...
}
server_name _ looks like a catch-all, but it is not a wildcard — it is just an invalid name that can never match a real Host header. Its only job is to be the default server's name. Once the redirect block became the first block, every host except www.snakken.app — including the apex — fell through to the redirect, which pointed back at the apex. Hence the loop.
The fix, and the invariant
The fix is one attribute: the static-site block is now explicitly listen 8080 default_server. Selection no longer depends on file order.
The invariant we take away: any nginx config with more than one server block must declare default_server explicitly. Relying on block order is correct until the first person adds a block, which is exactly when it stops being correct.
What we'd do differently
- An external uptime check would have caught this in seconds rather than depending on someone watching the deploy. A
301to yourself is about the easiest failure a probe can flag. - Redirect changes get a curl matrix before merge: apex, www, a deep path, and the health endpoint — four lines in CI that would have failed this change at review time.
Six minutes of downtime on a pre-launch landing page costs almost nothing — which is precisely why it is the right time to practice writing these.
// Published under CC BY 4.0 — take the patterns, cite the source. · ← All articles