Serve SPA fallback with 200 for routable paths in prod static serving - #6996
Serve SPA fallback with 200 for routable paths in prod static serving#6996FarhanAliRaza wants to merge 3 commits into
Conversation
Direct loads of valid dynamic-route URLs (e.g. /articles/7) in self-hosted prod returned HTTP 404 with the SPA fallback body, making them indistinguishable from genuinely unknown paths (bad for SEO, uptime monitors, and anything trusting status codes). PrecompressedStaticFiles now accepts a route matcher: when the html-mode 404.html fallback is hit for a path that matches the app's route table, it is served with status 200; unknown paths keep the 404 status. The backend-mounted frontend passes app.router directly. For the standalone prod static server (frontend-only mode), the compiler now persists the route table to .web/routes.json at compile time and the mount builds a matcher from it, falling back to the previous behavior when no manifest exists. Configured frontend_path prefixes are restored before matching since the mount strips them from request paths. Fixes reflex-dev#6983
Greptile SummaryThe PR updates production static serving so registered SPA routes receive HTTP 200 while unknown paths retain HTTP 404.
Confidence Score: 5/5The PR appears safe to merge. No blocking failure remains.
|
| Filename | Overview |
|---|---|
| reflex/utils/precompressed_staticfiles.py | Uses the route matcher to change only routable SPA fallback responses from 404 to 200 while retaining sidecar handling. |
| reflex/utils/exec.py | Loads the compiled route manifest and adapts route matching for synthetic 404 routes and frontend path prefixes. |
| reflex/compiler/compiler.py | Emits the registered page-route manifest into the generated web workspace. |
| reflex/app.py | Centralizes deduplicated page routes and passes the application router to the mounted frontend. |
| tests/units/utils/test_precompressed_staticfiles.py | Adds fallback-status, compression, and real-file regression tests; the previously requested helper docstring is present. |
Reviews (3): Last reviewed commit: "Cover corrupt routes manifest in get_rou..." | Re-trigger Greptile
Merging this PR will not alter performance
Comparing Footnotes
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 139ba3c7b4
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
All reported issues were addressed across 9 files
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
… test coverage - Exclude the compiler's synthetic 404 page route from the SPA-fallback status matcher in get_frontend_mount, so a literal /404 request keeps its 404 status (with regression tests for both the manifest-built and explicitly passed routers). - Add news fragments for reflex and reflex-base. - Assert Vary: Accept-Encoding on the 200 SPA-fallback responses in the gzip test cases. - Add the missing docstring on the _articles_router test helper and fix the _page_routes property docstring lint.
|
seems like this one supersedes #6469 ? can you check to see if there is any overlap or anything missed in the earlier PR, integrate it here, then close the old one |
Ports the one test scenario from PR reflex-dev#6469 that the superseding implementation did not already cover: a routes.json that fails to parse must disable the SPA-fallback router (return None) rather than raise.
All Submissions:
Type of change
Changes To Core Features:
In self-hosted prod (
reflex run --env prod), a direct load of a valid dynamic-routeURL (e.g.
/articles/7) returned HTTP 404 with the SPA-fallback body, making validdynamic URLs indistinguishable from genuinely unknown paths — bad for SEO, uptime
monitors, and anything trusting status codes.
PrecompressedStaticFilesnow accepts a route matcher: when Starlette's html-mode404.htmlfallback is hit for a path that matches the app's route table, it is servedwith status 200; unroutable paths keep the 404 status. The backend-mounted frontend
passes
app.routerdirectly; for the standalone prod static server (frontend-onlymode), the compiler persists the route table to
.web/routes.jsonat compile time andthe mount builds a matcher from it, falling back to the previous behavior when no
manifest exists (the manifest lives outside
build/client, so it is not publiclyserved). Configured
frontend_pathprefixes are restored before matching since themount strips them from request paths.
Covered by new unit tests (routable → 200 incl. precompressed sidecars, unknown → 404,
real files unaffected, manifest loading,
frontend_pathhandling) and an extendedtests/integration/test_precompressed_frontend.pywith anarticles/[id]page.closes #6983