Guides / Django users logged out after every deploy
Django users logged out after every deploy
Every time you deploy, all your users are logged out. Or they get logged out at random, as if the site forgets them. Sessions survive restarts only if both halves survive: the data (in the database, cache or cookie) and the key that signs it. A deploy that loses either one signs everyone out.
What it means
A session has two parts. The browser holds a session ID in a cookie. Django looks up the data for that ID on the server and checks it against a hash derived from SECRET_KEY. If the data is gone, or the key has changed, the user is anonymous again.
Why it happens
SECRET_KEYchanges on each start. Settings generate a random key,get_random_secret_key()is called insettings.py, or a fallback default is regenerated. Every deploy invalidates every session.- Each instance has a different key. With two servers, a user gets logged out whenever the load balancer sends them to the other one.
- Sessions live in memory or on local disk.
LocMemCache(per process, wiped on restart) with the cache session backend, orsessions.backends.fileon an ephemeral disk. - The session table is wiped because a SQLite database was replaced on deploy (see SQLite in production).
- Cookie settings don't match the way users reach the site:
SESSION_COOKIE_SECURE = Trueon http, aSESSION_COOKIE_DOMAINfor a different domain, orwwwversus the bare domain. - The cache evicts sessions (Redis or Memcached with a low memory limit).
The fix
Use one stable key everywhere, read from the environment:
SECRET_KEY = os.environ["DJANGO_SECRET_KEY"] # same value on every instance and every release
Store sessions somewhere that outlives a deploy and is shared by all instances. The simplest choice is the database (the default):
SESSION_ENGINE = "django.contrib.sessions.backends.db"
# or keep them in a shared cache with a database fallback:
# SESSION_ENGINE = "django.contrib.sessions.backends.cached_db"
Make the cookie settings match your site:
SESSION_COOKIE_SECURE = True # only if the site is https everywhere
SESSION_COOKIE_DOMAIN = None # leave unset unless you share cookies across subdomains
SESSION_COOKIE_AGE = 60 * 60 * 24 * 14
When you do need to change the key, move the old one into the fallback list so sessions stay valid (Django 4.1 and newer):
SECRET_KEY = os.environ["DJANGO_SECRET_KEY"]
SECRET_KEY_FALLBACKS = [os.environ["DJANGO_SECRET_KEY_OLD"]] if os.environ.get("DJANGO_SECRET_KEY_OLD") else []
Making it stick in production
- Run
python manage.py clearsessionsdaily so the session table doesn't grow without end. - Test it: log in, deploy, reload. If you're signed out, check the key first, then where the sessions are stored.
- Never generate a key in settings code; set it once as a secret. More: SECRET_KEY setting must not be empty.
- Behind HTTPS and a proxy, check the secure-cookie detection too: http links behind a proxy.
Or skip it
On Django Cloud your SECRET_KEY is stored once with your settings and every release gets the same value, and a rollback brings back the settings of that release. Keep sessions in Postgres and they survive deploys.
pip install djangocloud-cli
djangocloud deploy
Free plans are available for students, AWS Activate startups, open-source, non-profit and research projects. See how Django Cloud compares with other ways to host Django.
Django is a registered trademark of the Django Software Foundation. Django Cloud is not affiliated with or endorsed by it. Found a mistake in this guide? Tell us and we will fix it.