Skip to content

Enable SECURE_PROXY_SSL_HEADER on TEST/PROD (#1329)#1336

Merged
jonfroehlich merged 1 commit into
masterfrom
1329-secure-proxy-ssl-header
Jun 18, 2026
Merged

Enable SECURE_PROXY_SSL_HEADER on TEST/PROD (#1329)#1336
jonfroehlich merged 1 commit into
masterfrom
1329-secure-proxy-ssl-header

Conversation

@jonfroehlich

Copy link
Copy Markdown
Member

Closes #1329.

What

Adds, gated to the deployed environments:

if DJANGO_ENV in ('PROD', 'TEST'):
    SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')

Behind UW CSE's TLS-terminating Apache proxy the Django container is reached over plain HTTP, so request.scheme reported http and request.is_secure() was False even though visitors arrive over HTTPS. This tells Django to trust the proxy's X-Forwarded-Proto header as the real scheme.

Why it's safe (and why gated)

  • UW CSE IT (Jason Howe) confirmed Apache now sets X-Forwarded-Proto: https on both test and prod.
  • The Django backend binds to the host's loopback only, so a client can't reach it directly to forge the header.
  • Gated to DJANGO_ENV in (PROD, TEST) — local dev has no trusted proxy, so we must not trust a client-supplied header there. Mirrors the DJANGO_ENV-keyed pattern from Project page social media (opengraph) not working perfectly #1236.

Scope

This is the framework-level fix behind #1236. The in-app site_scheme workaround (context processor + website/utils/metadata.py + templates) is left in place for now as a redundant safety net; it will be removed in a follow-up once request.is_secure() is verified correct on -test.

Testing

No new automated test: the behavior is Django framework internals (SECURE_PROXY_SSL_HEADER) gated by an import-time env var, so a regression test would only re-test Django. Existing test_page_metadata.py site_scheme tests remain green (workaround untouched).

Manual verification after merge → deploy to -test:

  • Confirm canonical / og:url tags still render https:// (no regression).
  • Confirm request.is_secure() is now True behind the proxy.

🤖 Generated with Claude Code

Behind UW CSE's TLS-terminating Apache proxy, the Django container is
reached over plain HTTP, so request.scheme reported "http" and
request.is_secure() was False even though visitors arrive over HTTPS.

Apache now sets X-Forwarded-Proto: https on test and prod (confirmed by
UW CSE IT), and the backend binds to loopback only, so the header can't
be spoofed by a direct client. Trust it via SECURE_PROXY_SSL_HEADER,
gated to DJANGO_ENV in (PROD, TEST) — local dev has no such proxy and
must not trust a client-supplied header.

This is the framework-level fix behind #1236. The in-app site_scheme
workaround is left in place for now and will be removed in a follow-up
once verified on -test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@jonfroehlich jonfroehlich merged commit 81e2132 into master Jun 18, 2026
2 checks passed
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.

Enable SECURE_PROXY_SSL_HEADER so Django sees the real https scheme (supersedes in-app workaround)

1 participant