I was finally able to resolve with a combination of a few things:

  1. Removing SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') from the Django settings.py and instead using sslify to force https redirect in place of that.

  2. Making sure the trailing '/' was present in the original CORS request destination url which also causes a 3xx redirect.

  3. Doubling up the headers on the server side of AWS by adding 'Access-Control-Allow-Origin': '*','Access-Control-Allow-Methods': 'POST, OPTIONS','Access-Control-Allow-Headers': 'content-type', to the AWS_HEADERS setting in Django's settings.py.

  4. Adding another, likely redundant, https force within wsgi by adding os.environ['wsgi.url_scheme'] = 'https' to Django's settings.py. You can also do this by adding another https force os.environ['https'] = "on" within wsgi.py.

All of these things together proved to work, the last one is most likely redundant but sometimes the request would fail to be recognized as https without it. In all likelihood this could have been due to caching, but better safe than sorry.

Hopefully this helps someone like me who was dealing with this issue for a while without any clear solution.

Answer from Charles Morse on Stack Overflow
🌐
Readthedocs
django-secure.readthedocs.io › en › latest › settings.html
Settings Reference — django-secure 1.0.1.post1 documentation
A tuple of (“header”, “value”); if “header” is set to “value” in request.META, django-secure will tell Django to consider this a secure request. For example: SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTOCOL", "https")
🌐
Medium
medium.com › django-unleashed › how-to-secure-django-applications-with-https-and-ssl-a-comprehensive-guide-in-2024-f56b4ce11810
How to Secure Django Applications with HTTPS and SSL: A Comprehensive Guide in 2024 | by Samuel Getachew | Django Unleashed | Medium
September 18, 2024 - Update Django Settings: Open your settings.py file and make the following changes: SECURE_SSL_REDIRECT = True SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') CSRF_COOKIE_SECURE = True SESSION_COOKIE_SECURE = True
🌐
Django Documentation
docs.djangoproject.com › en › 5.2 › ref › middleware
Middleware | Django documentation | Django
If you are deployed behind a load-balancer or reverse-proxy server and Django can’t seem to tell when a request actually is already secure, you may need to set the SECURE_PROXY_SSL_HEADER setting.
🌐
Readthedocs
django-secure.readthedocs.io › en › v0.1.1 › settings.html
Settings Reference — django-secure 0.1.1 documentation
This setting, if set, should be a tuple of (“header”, “value”); if “header” is set to “value” in request.META, django-secure will tell Django to consider it a secure request (in other words, request.is_secure() will return True for this request).
🌐
Readthedocs
django-secure.readthedocs.io › en › v0.1.0 › settings.html
Settings Reference — django-secure v0.1.0 documentation
Usually in these cases the proxy ... should be a tuple of (“header”, “value”); if “header” is set to “value” in the request, django-secure will consider it a secure request. For example: SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTOCOL", "https"...
🌐
Django
docs.djangoproject.com › en › 6.0 › topics › security
Security in Django | Django documentation | Django
If necessary, set SECURE_PROXY_SSL_HEADER, ensuring that you have understood the warnings there thoroughly.
🌐
Reddit
reddit.com › r/django › http/https settings
r/django on Reddit: HTTP/HTTPS Settings
June 29, 2022 -

I'm using Django with AWS Application Load Balancer which holds the https/ssl certificate.

I want to terminate the SSL at the Load balancer, so that the load balancer will connect to the nginx/django via http.

However, I'm not sure how I can securely configure django to accept http connections from the load balancer.

I currently have these https settings:

    SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")

    CSRF_COOKIE_SECURE = True

    SESSION_COOKIE_SECURE = True
    SECURE_HSTS_SECONDS = 3600 # increase to 1 year eventually
    SECURE_SSL_REDIRECT = True #re enable in product
    SECURE_HSTS_INCLUDE_SUBDOMAINS = True

Is it safe to disable/remove any of these, if I have SSL termination at the load balancer?

Or if not, how can I safely put Django behind AWS Load balancer with docker and nginx?

CONCLUSION:

Finally managed to set this up ended up using load balancer(https aws certs) - > nginx (https own certs) - > django.

The managing two certs parts is made a lot easier by using private certs with nginx e.g. by generating them with openssl.

Find elsewhere
🌐
MDN Web Docs
developer.mozilla.org › en-US › docs › Learn_web_development › Extensions › Server-side › Django › web_application_security
Django web application security - Learn web development | MDN
TLS/HTTPS can be enabled on the ... a number of other protections you can use: SECURE_PROXY_SSL_HEADER can be used to check whether content is secure, even if it is incoming from a non-HTTP proxy....
🌐
Withlogic
withlogic.co › home › blog › 2025 › 01 › 21 › top django settings for secure deployments
Top Django settings for secure deployments | Blog with LOGIC
Therefore, HTTPS should be enforced with SECURE_PROXY_SSL_HEADER (set to ("HTTP_X_FORWARDED_PROTO", "https") ) along with SECURE_SSL_REDIRECT (set to True). The combination of these two settings will ensure that all URLs build by Django in production use the https protocol, while also understanding that the reverse proxy is using HTTPS and finally redirect HTTP requests to HTTPS.
🌐
Django Documentation
docs.djangoproject.com › en › 5.2 › ref › settings
Settings | Django documentation | Django
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https") This tells Django to trust the X-Forwarded-Proto header that comes from our proxy and that the request is guaranteed to be secure (i.e., it originally came in via HTTPS) when:
🌐
Toptal
toptal.com › developers › django › secure-django-heroku-pydantic-tutorial-part-4
Security in Django Applications: A Pydantic Tutorial, Part 4 | Toptal®
February 20, 2026 - Each Django setting is labeled with its corresponding security warning identifier as a code comment. The SECURE_PROXY_SSL_HEADER and SECURE_SSL_REDIRECT settings ensure our application only supports connection to our site via HTTPS, a far more ...
🌐
Readthedocs
django-secure.readthedocs.io › en › v0.1.1 › middleware.html
SecurityMiddleware — django-secure 0.1.1 documentation
If you are deployed behind a load-balancer or reverse-proxy server, and Django can’t seem to tell when a request actually is already secure, you may need to set the SECURE_PROXY_SSL_HEADER setting.
🌐
Django
code.djangoproject.com › ticket › 14597
#14597 (request.is_secure() should support headers like: X-Forwarded-Protocol and X-Forwarded-Ssl) – Django
The correct way to use it is to set it per-deployment to the specific header that you know is set+validated by the proxy server in that particular deployment, not to pre-emptively set it to a range of possible headers that some proxy servers might use. I'm going to implement this. I'm hoping to check something into trunk today, so that it'll get in for Django 1.4. ... Fixed #14597 -- Added a SECURE_PROXY_SSL_HEADER setting for cases when you're behind a proxy that 'swallows' the fact that a request is HTTPS
🌐
Django
code.djangoproject.com › ticket › 34855
#34855 (Document CSRF_TRUSTED_ORIGINS relation to SECURE_PROXY_SSL_HEADER.) – Django
It seems like the best solution here is to use SECURE_PROXY_SSL_HEADER to allow Django to determine if the client is using HTTPS or not, and use the "host" string https://subdomain.my-domain.com .
🌐
DjangoCFG
djangocfg.com › docs › fundamentals › configuration › security
DjangoCFG - AI-powered Django framework for professional SaaS platforms.
SECURE_SSL_REDIRECT = False # Proxy handles redirects SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') SESSION_COOKIE_SECURE = True # Production only CSRF_COOKIE_SECURE = True # Production only · Set ssl_redirect: bool = True only for bare metal Django without reverse proxy (causes redirect loops with nginx/Cloudflare).
🌐
Readthedocs
django-secure.readthedocs.io › en › latest › middleware.html
SecurityMiddleware — django-secure 1.0.1.post1 documentation
If this is your situation, you can set the SECURE_PROXY_SSL_HEADER setting to a tuple of (“header”, “value”); if “header” is set to “value” in request.META, django-secure will tell Django to consider it a secure request (in other words, request.is_secure() will return True for ...
🌐
OWASP Cheat Sheet Series
cheatsheetseries.owasp.org › cheatsheets › Django_Security_Cheat_Sheet.html
Django Security - OWASP Cheat Sheet Series
If your Django application is behind a proxy or load balancer, set the SECURE_PROXY_SSL_HEADER setting so that Django can detect the original request's protocol.
🌐
Medium
medium.com › @altafkhan_24475 › part-2-a-quick-guide-to-django-http-settings-094a0ceed768
Part-2: A Quick Guide to Django HTTP Settings | by Altaf Khan | Medium
June 9, 2024 - This setting in Django is used to inform Django about the header that signifies a request was made over HTTPS when your Django application is behind a reverse proxy or load balancer · SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')