From 3259fb3c858ab6753ac72f63dfc24882bb52044e Mon Sep 17 00:00:00 2001 From: Viktor Vaczi Date: Mon, 27 Jul 2026 14:54:13 +0200 Subject: [PATCH] fix(deploy/site): treat HSTS max-age=0 as a hard failure, not a variance MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enabling HSTS with the Max Age dropdown left at 0 serves `strict-transport-security: max-age=0`, which the sweep was filing under "differs from the baseline". That is far too mild: max-age=0 is not weaker protection, it is an instruction to browsers to DISCARD the HSTS policy they already hold — so it actively revokes the two-year policy Vercel had been setting, for every returning visitor. Now a hard failure regardless of EXPECT_HSTS, with the fix in the message, since nobody deliberately wants a header whose only effect is to turn protection off. A non-zero max-age that differs from the baseline stays a warning: protection is on, the duration is a judgement call. Caught on the live zone right after enabling HSTS. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01LAmkjM7okPdScp9XLW1JVr --- deploy/site/lib/parity.sh | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/deploy/site/lib/parity.sh b/deploy/site/lib/parity.sh index acfc54e..411fb44 100644 --- a/deploy/site/lib/parity.sh +++ b/deploy/site/lib/parity.sh @@ -247,13 +247,20 @@ assert_parity() { # once you have enabled it, and it becomes a hard assertion. _hsts="$(_hdr "$(_headers "$_base/")" strict-transport-security)" case "$_hsts" in + *max-age=0*) + # Distinct from — and worse than — absent. max-age=0 actively instructs + # browsers to DISCARD any HSTS policy they hold for this host, so it + # revokes the 2-year policy Vercel was setting for every returning + # visitor. Always a hard failure, regardless of EXPECT_HSTS: nobody + # deliberately wants a header whose only effect is to switch protection off. + fail hsts - "max-age=0 — HSTS is OFF and this actively clears the policy browsers already hold. Set Max Age to 12 months or 2 years in SSL/TLS -> Edge Certificates." ;; *max-age=63072000*) pass hsts - "$_hsts" ;; "") if [ "${EXPECT_HSTS:-0}" = 1 ]; then fail hsts - "absent, but EXPECT_HSTS=1" else soft hsts - "absent (Vercel sent max-age=63072000) — enable it in SSL/TLS -> Edge Certificates, then set EXPECT_HSTS=1" fi ;; - *) soft hsts - "$_hsts (differs from the Vercel baseline max-age=63072000)" ;; + *) soft hsts - "$_hsts (differs from the Vercel baseline max-age=63072000, but non-zero so protection is on)" ;; esac fi