Repository navigation
fix(nginx): restore /.well-known/, de-duplicate security headers, HSTS on www - #90
Merged
Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
/.well-known/ was 404ing.
location ~ /\.treats .well-known as adot-directory and denied it. The block sat inert in the repo for months
until af9a16a (2026-08-23) synced nginx.conf to the droplet for the first
time — the same deploy that broke /brand/*. That one was visible and got
fixed; this one was silent, because an unreachable JWKS does not fail
verification, it degrades keySource to embedded-fallback-jwks-unreachable
and still prints "VERIFIED — CRYPTOGRAPHICALLY INTACT".
Proven by same-file A/B: the snapshot at mmpm-cli/snapshots/
silver-flat-6czf-...-2026-07-17T03-20-38Z.json verified with keySource=jwks
on 2026-07-17 and reported the fallback on 2026-09-10 — same machine, same
verifier build, byte-identical input. Only the endpoint changed.
Blast radius: the compute provisioner passes MMPM_JWKS_URI= into
every provisioned customer substrate, so every customer's verifier was
falling back, with three SPEC-SNAPSHOT.md §6.4 case-(b) guarantees inactive
— JWKS revocation markers, the embedded-vs-published key byte-comparison,
and case-(c) kid-absent rejection.
Fixed with
location ^~ /.well-known/before the deny block. The ^~ isload-bearing: regex locations otherwise take precedence over prefix
locations, so a plain
location /.well-known/would still lose.Every response carried four security headers twice. nginx and
next.config.ts both set them: "DENY, DENY", "nosniff, nosniff", doubled
Referrer-Policy and Permissions-Policy. Not cosmetic — "nosniff" must be
the entire X-Content-Type-Options value, and browsers may discard a
multi-valued X-Frame-Options, so the duplication risked disabling the
protections it appeared to double. seo-headers.test.ts:61 already pinned
these four to the app, so the app keeps ownership and nginx's copies are
removed. nginx now owns only X-XSS-Protection, HSTS and CSP.
www→apex 301 served no HSTS. hstspreload.org requires redirects to carry
it and this domain is on the preload list.
Removed dead OCSP stapling. Let's Encrypt dropped OCSP URLs from certs on
2025-05-07 and shut its responders down on 2025-08-06; our certs carry no
OCSP URL, so ssl_stapling was a no-op and the resolver existed only to
serve it. Every proxy_pass here targets literal 127.0.0.1.
/_next/static/ additionally repeats X-XSS-Protection and HSTS: it declares
its own add_header, and per the nginx docs that suppresses inheritance
entirely. CSP is deliberately not repeated there — it is inert on a
subresource response.
Tests: src/nginx-security-headers.test.ts (16, new) and
src/nginx-wellknown-jwks.test.ts (7, new). Both proven load-bearing by
running them against the reconstructed pre-fix config — 10 of 16 fail.
Full suite green: 158 files, 2155 passed, 3 skipped.
Not included, each needing its own change:
lost 2026-06-29 zones needs a coordinated edit to the droplet's main
nginx.conf; adding limit_req alone fails nginx -t
listen ... http2→http2 on— needs the droplet's nginx version first(directive added in 1.25.1; unknown directive on anything older)
nginx -t not run locally (no nginx available); the deploy step validates
before reloading and restores on failure.