Flask uses SECRET_KEY to cryptographically sign the session cookie, so users can read it but cannot change it without the key. Make it long, random and kept out of source control. Since Flask 3.1 you can rotate it with SECRET_KEY_FALLBACKS, which lets old signatures stay valid while new ones use the new key.
Signed, not encrypted
The Flask quickstart describes sessions as implemented on cookies that are signed cryptographically. Its wording matters: the user could look at the contents of the cookie but not modify it, unless they know the secret key. So do not put anything in session that the user should not see, such as internal flags or another person's data.
If an attacker learns the key, they can mint a session for any user. Flask's own docs stress keeping it secret and say not to reveal it when posting questions or committing code. The configuration page adds that the key can be used for other security needs by extensions or your application, so a leak can reach beyond the session cookie.
Generating a good key
Flask's configuration docs suggest a one line command:
python -c 'import secrets; print(secrets.token_hex())'
Load it from the environment or a secret manager, not from a literal in the repository. A pattern that works:
import os
app.config["SECRET_KEY"] = os.environ["FLASK_SECRET_KEY"]
Using os.environ[...] rather than .get makes the app fail at startup when the key is missing, which is better than silently running with an empty or default value.
Rotating with SECRET_KEY_FALLBACKS
SECRET_KEY_FALLBACKS, added in Flask 3.1, is a list of old keys that can still be used for unsigning. The docs say this lets a project rotate keys without invalidating active sessions or other recently signed secrets. Order should not matter, though the default implementation tests the last key first, so the docs suggest ordering from oldest to newest.
Two cautions from the same page:
- Remove old keys after an appropriate period, because checking each extra key adds overhead.
- Flask's built-in session supports fallbacks, but extensions that use SECRET_KEY may not support this yet. Test every extension that signs data, such as CSRF protection or token libraries, before relying on a smooth rotation.
A rotation plan
| Step | Action |
|---|---|
| 1 | Generate the new key and store it in your secret manager |
| 2 | Set SECRET_KEY to the new key, add the old key to SECRET_KEY_FALLBACKS |
| 3 | Deploy all instances together |
| 4 | Confirm extensions that sign data still accept existing tokens |
| 5 | Remove the old key after sessions would have expired |
For a confirmed leak, skip the fallback. Anything signed with the old key stays acceptable while it is in the list, so an attacker with that key could still forge cookies. Rotate with no fallback and expect users to sign in again.
Related cookie settings
Flask also has settings for the session cookie itself. SESSION_COOKIE_HTTPONLY defaults to True. SESSION_COOKIE_SECURE defaults to False, so you must turn it on when serving over HTTPS. SESSION_COOKIE_SAMESITE defaults to None, and the docs recommend Lax or Strict.
Try it yourself
Click through it at your own pace, or open it full screen. Notice how a secret written into source code is reported as a finding.
Quick check
Can users read a Flask session cookie?
Yes. The cookie is signed, not encrypted, so the contents can be read but not altered without the key.
Which Flask version added SECRET_KEY_FALLBACKS?
Flask 3.1, according to the configuration documentation.
Is rotating with a fallback safe after a leak?
No. The old key still verifies data while it is listed. Rotate without a fallback.
Safeguard's SAST and dependency scanning can flag hardcoded secrets and outdated Flask versions in a repository. You can run a free scan.
Sources
Last reviewed: 19 October 2026.