Session Management
Flask provides a session mechanism that allows you to store information specific to a user from one request to the next. By default, Flask implements sessions using signed cookies, where the data is stored on the client's browser but cryptographically signed to prevent tampering.
Basic Session Usage
You interact with the session through the session proxy, which behaves like a standard Python dictionary.
from flask import Flask, session, redirect, url_for
app = Flask(__name__)
app.secret_key = "a-very-secret-key" # Required for signed cookies
@app.route("/login")
def login():
# Store data in the session
session["user_id"] = 42
return redirect(url_for("index"))
@app.route("/")
def index():
# Retrieve data from the session
user_id = session.get("user_id")
return f"User ID: {user_id}"
The Secret Key Requirement
If you attempt to write to the session without setting app.secret_key, Flask will raise a RuntimeError. Internally, when no secret key is provided, Flask uses a NullSession (defined in src/flask/sessions.py). This class allows read-only access to an empty state but fails on any modification with the message: "The session is unavailable because no secret key was set."
Session Persistence and Lifetimes
By default, Flask sessions are "browser sessions"—they expire when the user closes their browser. You can change this behavior to make sessions persist for a specific duration.
Permanent Sessions
To make a session persist across browser restarts, set session.permanent = True.
from datetime import timedelta
app.config["PERMANENT_SESSION_LIFETIME"] = timedelta(days=31)
@app.route("/login")
def login():
session.permanent = True
session["user_id"] = 42
return "Logged in"
The SessionMixin class in src/flask/sessions.py provides the permanent property, which internally toggles a _permanent key within the session dictionary. When this is set, Flask uses the PERMANENT_SESSION_LIFETIME configuration (defaulting to 31 days) to set the cookie's expiration date.
Tracking Changes and Nested Objects
Flask's default session object, SecureCookieSession, is a CallbackDict that automatically tracks when it is modified. However, it only detects changes to the top-level dictionary.
If you modify a mutable object stored inside the session (like a list or a nested dict), Flask will not know the session has changed and will not save the cookie unless you explicitly set session.modified = True.
@app.route("/add-item")
def add_item():
# Flask won't detect this change automatically
session["items"].append("new_item")
# Manually mark as modified so it gets saved
session.modified = True
return "Item added"
Internal Implementation: Signed Cookies
Flask uses the SecureCookieSessionInterface to manage sessions. This interface leverages the itsdangerous library to sign the session data.
- Opening: During the request context push (in
src/flask/ctx.py),si.open_sessionis called. It reads the cookie, verifies the signature usingitsdangerous.URLSafeTimedSerializer, and loads the data into aSecureCookieSessionobject. - Saving: At the end of the request,
si.save_sessionis called. Ifsession.modifiedisTrue, it re-signs the data and sets aSet-Cookieheader on the response.
The Vary: Cookie Header
Whenever you access the session proxy, Flask sets session.accessed = True. The SecureCookieSessionInterface.save_session method checks this flag and automatically adds a Vary: Cookie header to the response. This ensures that downstream caches (like CDNs) do not serve a session-specific response to the wrong user.
Customizing Session Serialization
Flask uses TaggedJSONSerializer (found in src/flask/json/tag.py) to convert session data to a string. This serializer supports extra types that standard JSON does not, such as datetime, UUID, and Markup.
You can extend this to support custom types by creating a JSONTag and registering it with the session interface. For example, to support OrderedDict:
from collections import OrderedDict
from flask.json.tag import JSONTag
class TagOrderedDict(JSONTag):
key = " od" # Unique tag key
def check(self, value):
return isinstance(value, OrderedDict)
def to_json(self, value):
# Convert to a list of pairs for JSON
return [[k, self.serializer.tag(v)] for k, v in value.items()]
def to_python(self, value):
return OrderedDict(value)
# Register the tag before the standard dict tag
app.session_interface.serializer.register(TagOrderedDict, index=0)
Custom Session Interfaces
If you need to store session data on the server (e.g., in Redis or a database) instead of in a client-side cookie, you can implement a custom SessionInterface.
A custom interface must implement two primary methods:
open_session(self, app, request): Returns a session object (must implementSessionMixin).save_session(self, app, session, response): Persists the session and updates the response (e.g., setting a session ID cookie).
from flask.sessions import SessionInterface, SessionMixin
class MySession(dict, SessionMixin):
pass
class MySessionInterface(SessionInterface):
def open_session(self, app, request):
sid = request.cookies.get(app.config["SESSION_COOKIE_NAME"])
if not sid:
return MySession()
# Load data from your data store using sid...
data = load_from_store(sid)
return MySession(data)
def save_session(self, app, session, response):
domain = self.get_cookie_domain(app)
path = self.get_cookie_path(app)
if not session:
# Handle session deletion...
return
# Save data to your data store and set a cookie with the ID...
sid = save_to_store(dict(session))
response.set_cookie(app.config["SESSION_COOKIE_NAME"], sid,
domain=domain, path=path)
app.session_interface = MySessionInterface()
Session Configuration Reference
The following configuration variables control session behavior:
| Configuration Key | Default | Description |
|---|---|---|
SESSION_COOKIE_NAME | 'session' | The name of the cookie used for the session. |
SESSION_COOKIE_DOMAIN | None | The domain for the session cookie. |
SESSION_COOKIE_PATH | None | The path for the session cookie. Defaults to APPLICATION_ROOT. |
SESSION_COOKIE_HTTPONLY | True | Prevents JavaScript from accessing the session cookie. |
SESSION_COOKIE_SECURE | False | Only sends the cookie over HTTPS. |
SESSION_COOKIE_SAMESITE | None | Restricts cookie sending on cross-site requests ('Lax' or 'Strict'). |
PERMANENT_SESSION_LIFETIME | timedelta(days=31) | Lifetime for permanent sessions. |
SESSION_REFRESH_EACH_REQUEST | True | If True, the cookie is refreshed on every request for permanent sessions. |
SECRET_KEY | None | The secret key used to sign the session cookie. |
SECRET_KEY_FALLBACKS | None | A list of old secret keys used to verify existing sessions during key rotation. |