Answer 404 for unrouted paths, and keep the route lists honest
web/nginx.conf ended its try_files chain in an unconditional /200.html, so every path the site does not have -- /wp-login.php, /.env, a typo'd inbound link -- came back as the SPA shell with a success status. It now answers 404, which needs nginx to know which routes exist: most it infers from the build output, but dynamic routes and ones that never opted into prerendering have no file on disk and are listed by hand. Those hand-maintained lists drift, and the drift is invisible until it ships: vite dev and npm run preview route from the client manifest and never read nginx.conf, so a new dynamic route works everywhere a developer would look and 404s in production. hack/check-web-routes.sh compares the lists against web/src/routes, and a workflow runs it. Its own workflow rather than another job on license-compliance.yml, which already carries one unrelated check. Also turns absolute_redirect off. With nginx's default the trailing-slash canonicaliser reconstructs the origin from its own listen port, so a request for https://demo.cairnobs.org/settings/ was answered with Location: http://127.0.0.1:3000/settings -- the container's internal address, unreachable from the client, and downgraded to http on the way. Verified by curl against the built image; it was latent here before the canonicaliser existed too, through the directory redirect on /dev.
This commit is contained in:
+27
-3
@@ -124,6 +124,30 @@ The repo convention prefers distroless/scratch base images. Serving a
|
||||
static SPA still needs *some* HTTP server, though, and `nginx:alpine` is
|
||||
the boring, standard choice for that job — writing a custom static-file
|
||||
binary just to stay distroless would be more engineering than a Phase 0
|
||||
placeholder page justifies. `nginx.conf` here is minimal: serve `build/`,
|
||||
fall back to `index.html` for client-side routing (only one route exists
|
||||
today, but this is what you want the moment a second one is added).
|
||||
placeholder page justifies. `nginx.conf` serves `build/`, and resolves a
|
||||
request in this order: the file itself, then the flat `<route>.html`
|
||||
adapter-static prerenders each route to, then — for the handful of routes
|
||||
that have no file on disk — the `200.html` SPA shell.
|
||||
|
||||
### 404s
|
||||
|
||||
Anything that matches none of the above answers **404**, not 200. That
|
||||
took explicit work, because the natural static-SPA config falls back to
|
||||
the shell unconditionally and hands every junk URL a success status;
|
||||
crawlers, uptime checks and vulnerability scanners then can't tell a real
|
||||
page from a miss. The 404 still *renders* the shell, so a human sees the
|
||||
app's own not-found page exactly as before — only the status line
|
||||
changed.
|
||||
|
||||
Two kinds of route legitimately have no file to match and so are named
|
||||
explicitly in `nginx.conf`: dynamic routes (`dashboards/[id]` and
|
||||
friends), whose params don't exist at build time, and routes that never
|
||||
opted into prerendering (`/data-sources`, which has no `+page.ts`). Those
|
||||
two allowlists are the only thing here that can drift out of sync with
|
||||
`src/routes` — and drift would break *only production*, since `vite dev`
|
||||
and `npm run preview` route from the client manifest and never read
|
||||
`nginx.conf`. `hack/check-web-routes.sh` fails CI when they disagree; run
|
||||
it after adding a dynamic or non-prerendered route.
|
||||
|
||||
Trailing slashes redirect (308) to the canonical no-slash form rather
|
||||
than 404ing, matching SvelteKit's default `trailingSlash: 'never'`.
|
||||
|
||||
Reference in New Issue
Block a user