How Flask Sessions Actually Work
Flask's built-in session object is not server-side storage by default — it's a dictionary-like structure that gets serialized, cryptographically signed with itsdangerous using app.secret_key, and sent to the browser as a single cookie. The signature prevents tampering (the browser can't forge or alter values without invalidating the signature), but the data itself is only base64-encoded, not encrypted, meaning anyone can decode and read the cookie's contents even though they can't modify it undetected — so sensitive data should never be placed directly in the session.
Cricket analogy: A signed-but-unencrypted session cookie is like a scorecard sealed with an official stamp: anyone can read the printed numbers on it, but nobody can alter a run total without breaking the tamper-evident seal — Flask's session is readable but not silently editable.
Configuring Secure Session Cookies
Several app.config keys govern session cookie behavior: SESSION_COOKIE_SECURE (only send the cookie over HTTPS), SESSION_COOKIE_HTTPONLY (prevents JavaScript from reading the cookie, mitigating XSS-based theft — enabled by default), SESSION_COOKIE_SAMESITE ('Lax' or 'Strict' to limit cross-site request forgery exposure), and PERMANENT_SESSION_LIFETIME (how long a session marked session.permanent = True remains valid). Setting session.permanent = True is required for PERMANENT_SESSION_LIFETIME to take effect at all; without it, Flask treats the cookie as a browser-session cookie that expires when the browser closes.
Cricket analogy: SESSION_COOKIE_HTTPONLY is like restricting who can physically handle the official scorebook — commentators can view the displayed score, but only the appointed scorer can touch the book itself, preventing unauthorized edits, just as HttpOnly blocks JavaScript from reading the cookie.
from datetime import timedelta
from flask import Flask, session
app = Flask(__name__)
app.secret_key = 'load-this-from-an-environment-variable'
app.config.update(
SESSION_COOKIE_SECURE=True, # only send over HTTPS
SESSION_COOKIE_HTTPONLY=True, # block JS access (default True)
SESSION_COOKIE_SAMESITE='Lax', # mitigate CSRF
PERMANENT_SESSION_LIFETIME=timedelta(days=7),
)
@app.route('/set-preference/<theme>')
def set_preference(theme):
session.permanent = True # required for PERMANENT_SESSION_LIFETIME to apply
session['theme'] = theme
return {'theme': session['theme']}, 200
@app.route('/clear-session')
def clear_session():
session.clear()
return {'message': 'session cleared'}, 200Server-Side Sessions with Flask-Session
Because the default client-side session is limited in size (cookies typically cap around 4KB) and visible to the client, applications that need to store larger or more sensitive session data often switch to the Flask-Session extension, which keeps the actual session payload in a backend store — Redis, filesystem, SQLAlchemy, or Memcached — and sends the browser only a small opaque session ID as the cookie. This flips the security model: the cookie itself no longer needs to be readable-but-safe, since it carries no meaningful data, and revoking a session becomes as simple as deleting its record from the backend store, which isn't possible with pure signed-cookie sessions since a previously issued cookie remains valid until it expires.
Cricket analogy: Switching to server-side sessions is like moving from carrying a physical printed pass with your details on it to a barcode that only references a record in the stadium's central database — lose the barcode and security can instantly deactivate that specific record, unlike a printed pass that stays valid until it physically expires.
Flask-Session's default filesystem backend is fine for local development but doesn't scale across multiple server processes or machines; production deployments typically use a shared backend like Redis so every app instance sees the same session store.
Client-side (default) Flask sessions cannot be forcibly invalidated server-side before their expiration — once issued, a signed cookie stays valid until it expires or the secret_key changes (which invalidates every session at once). If you need per-session revocation (e.g. a 'log out all devices' feature), you need server-side sessions or a token-tracking mechanism.
- Flask's default session is a signed, client-side cookie — tamper-evident via itsdangerous, but not encrypted, so its contents are readable by anyone with the cookie.
- Never store sensitive data (passwords, secrets, full PII) directly in the session; store only small, non-sensitive identifiers.
- SESSION_COOKIE_SECURE, SESSION_COOKIE_HTTPONLY, and SESSION_COOKIE_SAMESITE configure cookie transport and access security.
- session.permanent = True is required for PERMANENT_SESSION_LIFETIME to actually apply.
- Client-side sessions cannot be individually revoked before expiration; only rotating secret_key invalidates all sessions at once.
- Flask-Session enables server-side storage (Redis, filesystem, database) with only an opaque ID sent to the browser, allowing per-session revocation.
- Production deployments generally need a shared backend (like Redis) rather than the filesystem backend to work correctly across multiple server processes.
Practice what you learned
1. Is Flask's default session data encrypted?
2. What is required for app.config['PERMANENT_SESSION_LIFETIME'] to actually take effect?
3. What does SESSION_COOKIE_HTTPONLY protect against?
4. Why can't a default (client-side) Flask session be individually revoked before its expiration?
5. What does the Flask-Session extension change about how sessions work?
Was this page helpful?
You May Also Like
Flask-Login Explained
Learn how the Flask-Login extension manages user sessions, the UserMixin interface, and route protection with login_required.
User Authentication in Flask
Understand the core building blocks of authenticating users in Flask: password hashing, login forms, and session-backed identity.
Building REST APIs with Flask
Learn how to design and implement RESTful APIs in Flask using route decorators, HTTP verbs, and JSON responses.
Related Reading
Related Study Notes in Web Development
Browse all study notesWebSockets Study Notes
Web Development · 30 topics
Web DevelopmentWebAssembly Study Notes
WebAssembly · 30 topics
Web DevelopmentgRPC Study Notes
Protocol Buffers · 30 topics
Web DevelopmentSpring Boot Study Notes
Java · 30 topics
Web DevelopmentDjango Study Notes
Python · 30 topics
Web DevelopmentNext.js Study Notes
JavaScript · 30 topics