HTTP is stateless by defaultβthe server does not automatically remember whether two requests come from the same user.
Flask solves this problem through the Session mechanism, allowing web applications to "remember" the user's state.
* * *
## How Session Works
Flask uses SecureCookieSession by default, which works as follows:
* Session data is serialized, signed with a key, and stored in the browser's Cookie
* With each request, the browser automatically sends this Cookie
* After Flask verifies the signature, it restores the data as a Python dictionary for you to use
Users can see the content in the Cookie (because it is base64 encoded), but cannot tamper with itβbecause any modification will cause the signature verification to fail.

* * *
## Configure SECRET_KEY
To use Session, you must first set SECRET_KEYβthe key used for signing.
## Example
import secrets
from flask import Flask
app = Flask( __name__ )
# Generate a secure random key (only need to generate once during initial setup)
# secrets.token_hex() produces a 64-character hexadecimal random string
app.secret_key= secrets.token_hex()
# Or set a fixed value directly (convenient for development, but do not use in production)
# app.secret_key = "dev-secret-key-change-in-production"
> Important: SECRET_KEY must be kept confidential and sufficiently random. If the key is leaked, anyone can forge your application's Session data. In production, it should be read from environment variables or configuration files, not hardcoded in the code.
Command to generate a high-quality key:
$ python -c 'import secrets; print(secrets.token_hex())'192b9bdd22ab9ed4d12e236c78afcb9a393ec15f71bbf5dc987d54727823bcbf
* * *
## Read and Write Session
session is a dictionary-like object, with usage basically the same as Python dictionaries:
## Example
from flask import Flask, session, redirect, url_for, request
app = Flask( __name__ )
app.secret_key="dev-secret-key"
@app.route("/")
def index():
# Check if username exists in session
if"username"in session:
return f"""
Welcome back, {session}!
Your visit count: {session.get("visits", 0)}
Logout
"""
return"""
TUTORIAL Home
You are not logged in.
Go to Login
"""
@app.route("/login", methods=["GET","POST"])
def login():
if request.method=="POST":
username = request.form.get("username","")
if username:
# Store username in session
session= username
# Initialize visit counter
session=0
return redirect(url_for("index"))
return"""
Login to TUTORIAL
"""
@app.route("/logout")
def logout():
# Clear all data in session
session.clear()
# Or only delete a specific key:
# session.pop("username", None)
return redirect(url_for("index"))
* * *
## Permanent Session
By default, Session expires after the browser is closed (browser session level).
If you need a "remember me" function, you can mark the Session as permanent:
## Example
from datetime import timedelta
# Set permanent session lifetime (default 31 days)
app.config= timedelta(days=7)
@app.post("/login-remember")
def login_remember():
username = request.form.get("username")
if username:
session= username
# Mark as permanent session, still valid after browser closes
session.permanent=True
return redirect(url_for("index"))
* * *
## Message FlashingβOne-time Messages
After users submit a form, you need to display feedback like "Operation successful" or "Error occurred".
For this type of "display once then disappear" message, Flask provides the flash() mechanism.
## Example
from flask import Flask, flash, get_flashed_messages, redirect, render_template, request, url_for
app = Flask( __name__ )
app.secret_key="dev-secret-key"
@app.route("/post", methods=["GET","POST"])
def create_post():
if request.method=="POST":
title = request.form.get("title","").strip()
if not title:
# Save error message to flash, categorized as "error"
flash("Title cannot be empty","error")
return redirect(url_for("create_post"))
# Save success message to flash, categorized as "success"
flash(f"Article "{title}" published successfully!","success")
return redirect(url_for("create_post"))
return render_template("create_post.html")
@app.route("/flash-demo")
def flash_demo():
# Get all flashed messages (automatically cleared after reading)
messages = get_flashed_messages(with_categories=True)
return render_template("flash_demo.html", messages=messages)
Rendering flash messages in template:
## Example
<!DOCTYPE html