Fix Django "Session data corrupted" Warnings
Fix the Django warning Session data corrupted and users being logged out: find SECRET_KEY changes and mismatches between servers, use SECRET_KEY_FALLBACKS for rotation, and align session engines and serializers.
Problem statement
The logs show this warning, often in bursts after a deploy:
WARNING django.security.SuspiciousSession: Session data corrupted
and users report being logged out or losing their cart. Django signs session data with SECRET_KEY. When it reads a session and the signature does not verify, it treats the data as tampered with, logs this warning, and starts an empty session. Real tampering is rare. The usual cause is that the key used to sign the session is not the key used to read it: the secret changed, servers disagree about it, or the serialization format changed.
- Check that every web and worker process uses the same
SECRET_KEY(compare a hash of it, not the value). - If the key was rotated, add the old key to
SECRET_KEY_FALLBACKS(Django 4.1+) for one session lifetime. - Make sure the key is not generated at startup (for example
get_random_secret_key()in settings). - Keep
SESSION_SERIALIZERandSESSION_ENGINEconsistent across servers during deploys. - If the warning is isolated and rare, it may be a stale or malformed cookie; no action needed.
Step-by-step solution
1. Compare keys across processes safely
Never print the secret. Print a short hash from each process:
python manage.py shell -c "import hashlib; from django.conf import settings; print(hashlib.sha256(settings.SECRET_KEY.encode()).hexdigest()[:12])"
Run it on every server, in every container, and in Celery workers if they create sessions. Different hashes mean different keys: a missing environment variable on one host, a Secret updated on only some pods, or a settings module that falls back to a default.
2. Never generate the key at startup
This pattern logs everyone out on every restart, and with several Gunicorn workers each worker has its own key:
SECRET_KEY = os.environ.get("SECRET_KEY", get_random_secret_key()) # broken
Fail loudly instead:
SECRET_KEY = os.environ["SECRET_KEY"]
3. Rotate keys with fallbacks
Since Django 4.1, SECRET_KEY_FALLBACKS lets Django verify signatures made with older keys while signing new data with the current one:
SECRET_KEY = os.environ["SECRET_KEY"]
SECRET_KEY_FALLBACKS = [k for k in os.environ.get("SECRET_KEY_FALLBACKS", "").split(",") if k]
Deploy the new key with the old one in the fallbacks, keep it there for at least SESSION_COOKIE_AGE (two weeks by default), then remove it. Sessions, password reset tokens, and signed cookies keep working through the rotation. See Rotate Django SECRET_KEY Safely.
4. Keep session settings consistent during deploys
During a rolling deploy, old and new versions run at the same time. If the new version changes SESSION_ENGINE (for example from database to cache) or SESSION_SERIALIZER, sessions created by one version may be unreadable by the other. Change these in two steps, or accept a logout for active users and announce it.
With the signed_cookies engine, the whole session lives in the cookie and every verification depends on the key, so mismatches show up immediately and widely.
5. Decide if it is noise
A single warning now and then can come from a user with a very old cookie, a browser extension, or a bot sending junk cookies. If the hashes match across processes and nothing changed recently, it is safe to leave. Route django.security.SuspiciousSession to its own logger if you want to monitor volume without noise.
Verification check: after fixing, the count of Session data corrupted warnings should drop to near zero, and logging in on one server then hitting another (or restarting Gunicorn) should keep you logged in.
Explanation
Django serializes session data and signs it with an HMAC derived from SECRET_KEY and a salt. On read, it recomputes the signature and compares. Any change to the key, the salt, or the data produces a mismatch, which Django cannot distinguish from tampering, so it logs a SuspiciousSession and returns an empty session rather than trusting the data. For database and cache backends the signed value is stored server-side, but it is still verified with the key, which is why key mismatches affect every session engine.
Edge cases / notes
- Leaked key. If the old key was exposed, do not put it in fallbacks; rotate immediately and accept that users are logged out.
- Cache eviction. With the cache session engine, evicted sessions simply disappear (no warning) and users are logged out; use
cached_dbfor durability. - Multiple apps on one domain sharing a cookie name can overwrite each other's session cookies; set distinct
SESSION_COOKIE_NAMEvalues. - Celery tasks rarely touch sessions, but if they sign data (tokens), they need the same key and fallbacks.
Next steps
- If logouts happen on every deploy, see Fix Users Logged Out After a Django Deploy.
- Choose a session backend with Configure Django Sessions for Production.
FAQ
Does Session data corrupted mean I was hacked?
Rarely. It usually means the signing key changed or differs between servers.
How do I change SECRET_KEY without logging everyone out?
Use SECRET_KEY_FALLBACKS with the old key for one session lifetime, then remove it.
Why are users logged out after every restart?
The key is probably generated at startup. Load a fixed key from the environment.
Can I silence the warning?
You can filter the logger, but first confirm all processes share the same key.