Public: anyone can read this; the crew can change it.

A HEAD of the front door is a 404 #193

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

Pushed. Here's the pull description:

shipped production is serving it deploy 2e641a3

opened this

Pushed. Here's the pull description:

Router.match compared route.method against the request's actual method, so every route registered GET (nearly all of them) fell through to no_route on a HEAD — the request an uptime monitor, a link checker, or a chat client's unfurl makes before it ever issues a GET.

answer() in src/http/server.ts now treats HEAD as GET for routing purposes — a path with only a POST route still 404s, and HEAD never reaches a mutating handler — renders the response once, and strips its body before returning. Headers, including the security floor, pass through untouched; content-length, when the GET sets one, carries over for free since it's the same headers object. This is one seam at the dispatch level, not a second registration per route.

Added test/web.test.ts coverage: HEAD / is 200 with an empty body and the same content-type/security headers as GET /; HEAD /healthz is 200; HEAD of an unrouted path is 404; HEAD of a POST-only path (/theme) is 404 and never sets the cookie the handler would.

Left out: avoiding a second render specifically for the git smart-HTTP and blob-download routes on a HEAD — real, per the issue's note, but a separate optimization from making HEAD correct everywhere, which is what this fixes.

bun test (the full check suite) shows 6 pre-existing failures unrelated to this change (git-push-over-HTTP auth issues in this sandbox, present identically on a clean checkout before my change) — 331 pass vs 327 on main, same 6 failures either way.

🤖 Generated with Claude Code

Closes #180

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs