fix(app): restrict default CORS origins to deploy_url in prod mode

FA
1d ago
+170-11

All Submissions:

  • Have you followed the guidelines stated in CONTRIBUTING.md file?
  • Have you checked to ensure there aren't any other open Pull Requests for the desired changed?

Type of change

  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

Changes To Core Features:

  • Have you added an explanation of what your changes do and why you'd like us to include them?
  • Have you written new tests for your core changes, as applicable?
  • Have you successfully ran tests with your changes locally?

Description

closes #7640

The ["*"] default for cors_allowed_origins let any site connect to the backend. This PR makes the default mode-aware:

  • Dev mode: unchanged, any origin is allowed.
  • Prod mode: the default narrows to the origin of deploy_url. If deploy_url is None, no cross-origin request is accepted.
  • Explicit cors_allowed_origins (from rxconfig.py or REFLEX_CORS_ALLOWED_ORIGINS) is used as is in both modes, so ["*"] remains an opt-in to the old behavior. Explicitness is detected via Config._non_default_attributes, not by comparing against the default tuple.

Both enforcement points, the Starlette CORSMiddleware and the Socket.IO AsyncServer, now resolve their origins through one helper, _cors_allowed_origins() in reflex/app.py. Passing the list straight through to engineio is equivalent to the old "*" special case, since engineio treats a list containing "*" as allow-all.

deploy_url is reduced to an origin serialized the way browsers serialize the Origin header: lowercase host, no path, and the scheme's default port omitted (https://Example.COM:443/app → https://example.com). Non-default ports and IPv6 hosts are kept.

Notes

  • Integration tests are unaffected: AppHarness pins dev mode before building the app, so AppHarnessProd still resolves to ["*"].
  • With reflex run --env prod and the default deploy_url of http://localhost:3000, opening the app via 127.0.0.1:3000 is now rejected. Set deploy_url or cors_allowed_origins for alternate origins.

Tests

Unit tests cover dev vs prod, path-bearing and default-port deploy_url forms, unset deploy_url, explicit lists, explicit ["*"] via kwargs and via env, and request-level enforcement: a preflight through Starlette's TestClient from the deploy_url origin passes while other origins are refused, with the same cases checked against Engine.IO's origin resolution. News fragments added for reflex (breaking) and reflex-base (docs).

https://claude.ai/code/session_01C1xrGYmKsaacgEafTz9BTJ