Using Signed Cookie Sessions
Flask uses signed cookies for session management by default. This implementation relies on the SecureCookieSessionInterface to handle the serialization and signing of session data, and the SecureCookieSession class to track the state of the session during a request.
The Session Object
The SecureCookieSession class (found in src/flask/sessions.py) is the object you interact with when using flask.session. It inherits from werkzeug.datastructures.CallbackDict and SessionMixin.
State Tracking
The session object tracks two primary states: modified and accessed.
accessed: This flag is set toTruewhenever a key is read from the session. Whenaccessedis true, Flask automatically adds aVary: Cookieheader to the response. This informs caching proxies that the response content may vary based on the user's session cookie.modified: This flag is set toTruewhenever the session dictionary is updated (e.g.,session["user_id"] = 1). Flask only writes the session cookie back to the client ifmodifiedis true.
Manual Modification Tracking
Because SecureCookieSession only tracks top-level changes to the dictionary, it cannot detect when mutable objects stored inside the session are changed. If you modify a nested list or dictionary, you must manually set the modified flag:
@app.route("/update_cart")
def update_cart():
# Modifying a nested list
session["cart"].append("item_id")
# Manual flag required for the change to be saved
session.modified = True
return "Cart updated"
The Session Interface
The SecureCookieSessionInterface manages the lifecycle of the session cookie. It is responsible for opening the session at the start of a request and saving it at the end.
Opening and Saving Sessions
open_session: Reads the session cookie from the request. It uses a signer to verify the signature and deserialize the data. If the signature is invalid or the cookie is missing, it returns a new, emptySecureCookieSession.save_session: Determines if the session needs to be saved based on themodifiedflag and configuration. It serializes the session data, signs it, and sets theSet-Cookieheader on the response.
Cryptographic Signing
Flask uses the itsdangerous module for signing. The get_signing_serializer method in SecureCookieSessionInterface creates a URLSafeTimedSerializer using the application's SECRET_KEY.
# Internal implementation detail from src/flask/sessions.py
def get_signing_serializer(self, app: Flask) -> URLSafeTimedSerializer | None:
if not app.secret_key:
return None
# ... setup keys and signer_kwargs ...
return URLSafeTimedSerializer(
keys,
salt=self.salt,
serializer=self.serializer,
signer_kwargs={
"key_derivation": self.key_derivation,
"digest_method": self.digest_method,
},
)
Security and Key Management
A SECRET_KEY is mandatory for signed sessions. If no secret key is set, Flask uses a NullSession, which raises a RuntimeError if you attempt to modify it.
Key Rotation
You can rotate secret keys without invalidating existing sessions by using SECRET_KEY_FALLBACKS. When verifying a session cookie, Flask will try the current SECRET_KEY first, then iterate through the fallback keys.
app.secret_key = "current-strong-key"
app.config["SECRET_KEY_FALLBACKS"] = ["old-key-1", "old-key-2"]
Serialization with Tagged JSON
Flask uses a specialized TaggedJSONSerializer (defined in src/flask/json/tag.py) for session data. This allows the session to store more Python types than standard JSON, including:
tuplebytesdatetimeobjectsuuid.UUIDmarkupsafe.Markup
These types are "tagged" during serialization so they can be reconstructed accurately when the session is loaded. For example, a tuple is stored with a t tag to distinguish it from a list.
Configuration
The behavior of the session cookie is controlled by several configuration variables:
| Config Key | Description |
|---|---|
SESSION_COOKIE_NAME | The name of the cookie (default: 'session'). |
SESSION_COOKIE_DOMAIN | The domain for the cookie. |
SESSION_COOKIE_PATH | The path for the cookie. |
SESSION_COOKIE_HTTPONLY | Prevents client-side scripts from accessing the cookie (default: True). |
SESSION_COOKIE_SECURE | Only sends the cookie over HTTPS (default: False). |
SESSION_COOKIE_SAMESITE | Restricts cookie sending on cross-site requests (default: 'Lax'). |
PERMANENT_SESSION_LIFETIME | How long a permanent session lasts (default: 31 days). |
SESSION_REFRESH_EACH_REQUEST | Whether to send the cookie on every request to refresh the expiration. |
You can customize these in your application config:
app.config.update(
SESSION_COOKIE_SECURE=True,
SESSION_COOKIE_HTTPONLY=True,
SESSION_COOKIE_SAMESITE='Lax',
)