Skip to main content

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:

  1. 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.
  2. 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 a Set-Cookie header).

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.

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 to True whenever a key is set or deleted. Flask only calls save_session if this is True (or if the session is permanent).
  • accessed: Set to True whenever the session is read. This is used to add a Vary: Cookie header to the response, ensuring that caches don't serve session-specific content to other users.
  • permanent: If True, the session will expire after PERMANENT_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 KeyDefaultDescription
SESSION_COOKIE_NAME"session"The name of the cookie.
SESSION_COOKIE_DOMAINNoneThe domain for the cookie. If None, it's valid for the exact domain only.
SESSION_COOKIE_PATHNoneThe path for the cookie. Falls back to APPLICATION_ROOT.
SESSION_COOKIE_HTTPONLYTruePrevents JavaScript from accessing the cookie.
SESSION_COOKIE_SECUREFalseOnly sends the cookie over HTTPS.
SESSION_COOKIE_SAMESITE"Lax"Controls cross-site request behavior (Lax, Strict, or None).
SESSION_REFRESH_EACH_REQUESTTrueIf permanent, sends the cookie on every response to refresh the expiry.