Skip to content

fix(security): send the security headers with 404 pages - #400

Merged
mindsers merged 2 commits into
mainfrom
fix/not-found-security-headers
Oct 13, 2026
Merged

mindsers merged 2 commits into
mainfrom
fix/not-found-security-headers

Conversation

@mindsers

Copy link
Copy Markdown
Contributor

Description

The page for an unknown path leaves without any of the security headers the rest of the app sends.

When no route matches, React Router answers the 404 before any middleware runs. securityHeaders() sits on the root route, so it never sees those responses: no X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy, HSTS or CSP. The root loader does not run either, so the page's scripts carry no CSP nonce, which would break it the day the policy stops being report-only.

On a production build of main:

Request Status X-Frame-Options CSP
GET /login 200 DENY present
GET /anything-unknown 404 missing missing

Fix. A catch-all route (shell/not-found.tsx), registered last, matches those paths and answers 404 from its loader and its action. The root middleware and loader now run, and the root ErrorBoundary renders the same page as before. It needs a component, otherwise it would be a resource route and the 404 would leave as an empty response; it needs an action, otherwise a form posted to an unknown path would answer 405.

As a side effect, the server stops logging a No route matches URL stack trace for every unknown path.

Verified on a production build of this branch, in multi-tenant mode with Host headers. Every row carries the headers:

Request Answer
known.<domain>/nope, /settings/nope/deeper, /assets/nope.js, /nope.data 404
POST known.<domain>/nope (same origin, or no Origin) 404
<domain>/nope (base domain) 404
unknown.<domain>/nope 302 → /congregation-not-found
unknown.<domain>/congregation-not-found 200
known.<domain>/login 200

Tests. The E2E spec was written first and failed on the missing X-Frame-Options; it also checks the not-found page still renders. A unit test covers the route's loader and action.

Type de changement

  • Bug fix
  • Nouvelle fonctionnalité
  • Refactoring (pas de changement fonctionnel)
  • Documentation
  • Autre : ___

Checklist

  • J'ai formaté mon code avant de pousser (pnpm build:format)
  • J'ai mis à jour la documentation si nécessaire

Issue liée

None.

When no route matches, React Router answers the 404 before any middleware
runs. The page for an unknown path therefore left without X-Frame-Options,
X-Content-Type-Options, Referrer-Policy, Permissions-Policy and the CSP that
the root middleware gives every other response, and its scripts without the
CSP nonce, since the root loader did not run either.

A catch-all route, registered last, now matches those paths and answers 404
from its loader and action. The root middleware and loader run, and the root
ErrorBoundary renders the same page as before. The server also stops logging
a "No route matches URL" stack trace for every unknown path.
@mindsers
mindsers merged commit c860b49 into main Oct 13, 2026
7 checks passed
@mindsers
mindsers deleted the fix/not-found-security-headers branch October 13, 2026 19:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant