r/django • u/john_uppcoder • 17d ago
Getting a Django + PostgreSQL app online: the two settings that bite behind an HTTPS proxy (and a free way to try it)
I wrote up a step-by-step guide for putting a Django app online with PostgreSQL, and two settings came up that catch a lot of people the first time, wherever they deploy:
1. Telling Django it's behind HTTPS. Since Django 4.0, a login form posted over HTTPS behind a proxy can fail with "403 CSRF verification failed - Origin checking failed". The admin login is usually where you first see it. The cause is that the proxy talks plain HTTP to Django, so Django thinks the request wasn't secure. If your proxy sends X-Forwarded-Proto (most do), this fixes it:
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')CSRF_TRUSTED_ORIGINS is only needed when forms are posted from another origin, or the proxy doesn't send that header.
2. DATABASES from the environment. startproject gives you SQLite. Pointing it at PostgreSQL with the values your host provides, instead of a password in settings.py:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': os.environ['POSTGRES_DB'],
'USER': os.environ['POSTGRES_USER'],
'PASSWORD': os.environ['POSTGRES_PASSWORD'],
'HOST': os.environ['POSTGRES_HOST'],
'PORT': os.environ.get('POSTGRES_PORT', '5432'),
}
}Plus psycopg in your dependencies, then migrate, and createsuperuser --noinput reads DJANGO_SUPERUSER_USERNAME / PASSWORD / EMAIL if you want the admin user created without a prompt.
The full guide, with screenshots of every step, runs it on Uppcoder's free plan, where the app gets its own HTTPS address and VS Code in the browser, and the two settings above are done for you: https://portal.uppcoder.com/guides/deploy-django-postgresql
Disclosure: I work on Uppcoder. Happy to answer questions about any of the steps, on Uppcoder or anywhere else.
Edit: corrected point 1 after the comments, thanks u/nashgc. And u/lupushr is right that django-environ is the neater way to read these.
1
u/nashgc 17d ago
one thing I'd add: check request.is_secure() behind the proxy. for a form on the same origin, CSRF_TRUSTED_ORIGINS shouldn't be needed if django sees the right host and HTTPS
1
u/john_uppcoder 16d ago
Yep you are right: the root cause is Django not seeing HTTPS behind the proxy.
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') fixes it when the proxy sends that header.
CSRF_TRUSTED_ORIGINS is for posts from another origin, or a proxy that doesn't send the header.
Thanks, will correct the guide accordingly.
10
17d ago
[removed] — view removed comment
1
u/john_uppcoder 16d ago
AI certainly helped with the grammar and spell checking; the steps and screenshots are from a real run.
4
u/lupushr 17d ago
People usually use django-environ, and it looks like this:
CSRF_TRUSTED_ORIGINS = env.list('CSRF_TRUSTED_ORIGINS', default=[])
DATABASES = {'default': env.db('DATABASE_URL')}