Introduction to Sessions in Flask
Flask manages user sessions through a pluggable architecture defined by the SessionInterface. This system allows you to store data that persists across multiple requests from the same client, either by storing that data in a signed cookie (the default) or by using a server-side backend like Redis or a database.
The Session Lifecycle
Flask handles sessions automatically as part of the request-response cycle. This lifecycle is managed by two primary methods in the SessionInterface class found in flask.sessions:
open_session(app, request): Called at the beginning of each request when the request context is pushed. It retrieves the session data (e.g., from a cookie) and returns a session object.save_session(app, session, response): Called at the end of the request, after the view function has returned a response but before the response is sent to the client. It persists the session data (e.g., by setting aSet-Cookieheader).
Internally, RequestContext.push() in flask.ctx triggers _get_session(), which calls the interface's open_session. If this method returns None, Flask calls make_null_session() to create a NullSession.
Default Cookie-Based Sessions
By default, Flask uses SecureCookieSessionInterface. This implementation does not store data on your server; instead, it serializes the session dictionary, signs it cryptographically to prevent tampering, and stores it in a browser cookie.
To use the default session, you must set a SECRET_KEY in your application configuration. If you attempt to modify the session without a secret key, Flask will raise a RuntimeError because it cannot securely sign the cookie.
from flask import Flask, session
app = Flask(__name__)
app.secret_key = "a-very-secret-phrase"
@app.route("/set")
def set_session():
session["user_id"] = 42
return "Session set"
@app.route("/get")
def get_session():
return f"User ID: {session.get('user_id')}"
The default implementation uses itsdangerous.URLSafeTimedSerializer for signing and flask.json.tag.TaggedJSONSerializer for serialization, which supports extra types like UUID and datetime.
Tracking Session State
The session object in Flask is not a plain dictionary; it implements SessionMixin, which adds several tracking properties:
modified: Set toTruewhenever a key is set or deleted. Flask only callssave_sessionif this isTrue(or if the session ispermanent).accessed: Set toTruewhenever the session is read. This is used to add aVary: Cookieheader to the response, ensuring that caches don't serve session-specific content to other users.permanent: IfTrue, the session will expire afterPERMANENT_SESSION_LIFETIME(default 31 days) instead of when the browser closes.
Gotcha: Nested Modifications
Flask's default session tracker only detects top-level changes. If you modify a mutable object stored inside the session, you must manually mark the session as modified:
@app.route("/add-item")
def add_item():
# Flask won't see this change automatically
session["cart"].append("apple")
# You must set this manually
session.modified = True
return "Item added"
Custom Session Interfaces
You can replace the default session behavior by assigning a custom class to app.session_interface. This is how extensions like Flask-Session implement server-side storage.
A custom interface must implement open_session and save_session. If your interface requires the request to be matched to a URL (to check request.endpoint), you must call app_ctx.match_request() manually inside open_session, as routing normally happens after the session is opened.
from flask.sessions import SessionInterface, SessionMixin
class MySession(dict, SessionMixin):
pass
class MySessionInterface(SessionInterface):
def open_session(self, app, request):
# Example: Load session from a custom header or database
sid = request.headers.get("X-Session-ID")
if not sid:
return MySession()
# Load data from your backend here...
return MySession(user_id=123)
def save_session(self, app, session, response):
# Example: Save session back to your backend
if not session:
return
# Persist data here...
response.headers["X-Session-ID"] = "some-id"
app = Flask(__name__)
app.session_interface = MySessionInterface()
Configuration Reference
The SessionInterface uses several configuration variables to control cookie behavior. These are accessed via helper methods like get_cookie_name(app) and get_cookie_domain(app).
| Config Key | Default | Description |
|---|---|---|
SESSION_COOKIE_NAME | "session" | The name of the cookie. |
SESSION_COOKIE_DOMAIN | None | The domain for the cookie. If None, it's valid for the exact domain only. |
SESSION_COOKIE_PATH | None | The path for the cookie. Falls back to APPLICATION_ROOT. |
SESSION_COOKIE_HTTPONLY | True | Prevents JavaScript from accessing the cookie. |
SESSION_COOKIE_SECURE | False | Only sends the cookie over HTTPS. |
SESSION_COOKIE_SAMESITE | "Lax" | Controls cross-site request behavior (Lax, Strict, or None). |
SESSION_REFRESH_EACH_REQUEST | True | If permanent, sends the cookie on every response to refresh the expiry. |