Behind a reverse proxy, your Python app sees the proxy's address and plain HTTP unless you tell it to trust forwarded headers. Django uses SECURE_PROXY_SSL_HEADER, Flask uses Werkzeug's ProxyFix, and FastAPI uses the forwarded-allow-ips option. Trust only your own proxy, because clients can forge these headers.
The problem they all solve
A proxy terminates HTTPS and forwards requests to your app over a local connection. Without help, the app thinks every request is HTTP from the proxy's IP. That breaks redirects, absolute URLs, secure cookie logic and CSRF checks. Proxies add X-Forwarded headers to pass on the real values. The risk is that a client can send the same headers, so the app must know which ones come from a trusted hop.
Django
Django's settings reference explains that is_secure() normally checks for an https URL, and that this matters for CSRF protection. If a proxy swallows the original scheme, you configure the proxy to set a header and tell Django what to look for:
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
The docs warn that this tells Django to trust the header and treat the request as secure. So the proxy must set or strip the header on every request, and the app server must not be reachable except through the proxy. Otherwise a client could claim a plain HTTP request was HTTPS.
Flask
Flask's proxy page says the app can be told to trust X-Forwarded headers by wrapping it with Werkzeug's ProxyFix middleware, configured with how many proxies are chained in front:
from werkzeug.middleware.proxy_fix import ProxyFix
app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1, x_proto=1, x_host=1, x_prefix=1)
The page is emphatic: only use it if you are actually behind a proxy, set the correct number of proxies for each header, and note that incoming headers can be faked. It states that getting this wrong can be a security issue.
FastAPI
FastAPI's behind a proxy page describes the CLI option forwarded-allow-ips, which takes the IP addresses trusted to supply forwarded headers. Setting it to a star trusts every incoming IP. The docs say that is acceptable when only the proxy talks to the server. If the app is reachable another way, star lets any client set those headers.
Side by side
| Framework | Setting | Main risk if wrong |
|---|---|---|
| Django | SECURE_PROXY_SSL_HEADER | Client spoofs HTTPS, weakening CSRF and cookie logic |
| Flask | ProxyFix with counts per header | Spoofed client IP, host or scheme |
| FastAPI | forwarded-allow-ips | Any IP can set forwarded headers if star is used with direct exposure |
How to set it safely
- Draw the path: client, proxy, app. Count the hops.
- Make the proxy overwrite forwarded headers instead of appending client supplied ones.
- Bind the app to a private interface or restrict it with network rules so only the proxy connects.
- Set the framework option to trust exactly that hop, not every address.
- Test by sending your own X-Forwarded-For and X-Forwarded-Proto directly to the app port. The app should ignore them.
Related settings matter too. Django needs ALLOWED_HOSTS and CSRF_TRUSTED_ORIGINS to match the public hostname, and Flask's security page recommends host header validation.
Try it yourself
Click through it at your own pace, or open it full screen. See how a scan looks at the app from the outside, where header handling shows up.
Quick check
Is trusting every forwarded header with a star always wrong?
Not if only your proxy can reach the app. It is risky the moment the app is reachable another way.
Which header does Django look at by default?
None. SECURE_PROXY_SSL_HEADER defaults to None, so you must set it.
Safeguard's asset discovery and DAST scanning can help find app servers that are reachable without the proxy. Try a free scan.
Sources
- Django settings reference, SECURE_PROXY_SSL_HEADER
- Flask, tell Flask it is behind a proxy
- FastAPI, behind a proxy
- Flask security considerations
Last reviewed: 19 October 2026.