Repository navigation
fix(security): send the security headers with 404 pages - #400
Merged
Merged
Conversation
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.
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.
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: noX-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:X-Frame-OptionsGET /loginDENYGET /anything-unknownFix. 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 rootErrorBoundaryrenders 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 URLstack trace for every unknown path.Verified on a production build of this branch, in multi-tenant mode with
Hostheaders. Every row carries the headers:known.<domain>/nope,/settings/nope/deeper,/assets/nope.js,/nope.dataPOST known.<domain>/nope(same origin, or noOrigin)<domain>/nope(base domain)unknown.<domain>/nope/congregation-not-foundunknown.<domain>/congregation-not-foundknown.<domain>/loginTests. 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
Checklist
pnpm build:format)Issue liée
None.