Skip to content

Add a csrf_trusted_origins setting for reverse proxies - #978

Merged
ajslater merged 1 commit into
developfrom
csrf-trusted-origins
Oct 19, 2026
Merged

ajslater merged 1 commit into
developfrom
csrf-trusted-origins

Conversation

@ajslater

Copy link
Copy Markdown
Owner

Summary

A user behind a reverse proxy got CSRF 403s and worked around them by patching CsrfViewMiddleware out of settings. That is unsafe, and it doesn't fix the API either: DRF's SessionAuthentication enforces CSRF on its own. Codex never set Django's CSRF_TRUSTED_ORIGINS, so there was no supported fix when a proxy serves Codex under a different scheme or host.

  • New [server] csrf_trusted_origins (TOML array) / CODEX_CSRF_TRUSTED_ORIGINS (comma-separated) feeds CSRF_TRUSTED_ORIGINS. The default is empty, so existing installs behave the same.
  • get_str_list in codex/settings/config.py reads a TOML array or a comma string. normalize_csrf_trusted_origins strips a trailing slash, keeps https://*.example.com wildcards, dedupes in order, and drops a scheme-less entry (it could never match an Origin header) with a warning that says to check X-Forwarded-Proto / X-Forwarded-Host first.
  • Docs: a commented example in codex.toml.default and in the README copy of it, the env var in the README list, and a new "CSRF 403 behind a proxy" section under Reverse Proxy that tells users to check the forwarded headers first and not to remove the middleware.
  • NEWS: a Features bullet in the unreleased v2.5.5 section.

Origins are host names, so they stay out of the telemeter.

Test plan

  • tests/test_csrf_trusted_origins.py: loader and normalizer cases, env-over-TOML precedence (including an empty env value), the warning on a scheme-less entry, and an end-to-end Client(enforce_csrf_checks=True) PUT /api/v4/auth/timezone with a mismatched Origin: 403 CSRF Failed … Origin checking failed without the setting, 204 with it.
  • Settings import gives CSRF_TRUSTED_ORIGINS == [] with the setting unset; with the env var set, it normalizes the list and logs the warning.
  • make fix, make lint, make ty clean. The two existing cover.py "unreachable" warnings are untouched.
  • make test-python: 1683 passed, 1 xfailed.

🤖 Generated with Claude Code

A user behind a reverse proxy got CSRF 403s and worked around them by
removing CsrfViewMiddleware. That is unsafe and doesn't even work for the
API: DRF's SessionAuthentication enforces CSRF itself. Codex never set
Django's CSRF_TRUSTED_ORIGINS, so a proxy that serves Codex under another
scheme or host (usually one missing X-Forwarded-Proto) had no fix.

[server] csrf_trusted_origins (TOML array) or CODEX_CSRF_TRUSTED_ORIGINS
(comma-separated) now feeds CSRF_TRUSTED_ORIGINS. Entries lose a trailing
slash and are deduped; a scheme-less entry, which could never match an
Origin header, is dropped with a warning that points at the
X-Forwarded-* headers first. The default is empty, so existing installs
are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ajslater
ajslater merged commit 3463ffb into develop Oct 19, 2026
9 checks passed
@ajslater
ajslater deleted the csrf-trusted-origins branch October 19, 2026 03:06
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