9. Sessions, flash messages, and redirects

HTTP has no memory. Each request arrives knowing nothing about the last one, which is a problem the moment you want to remember a logged-in user or show a "saved successfully" message after a redirect. Flask's answer is the session: a small, signed cookie that survives from one request to the next. This page is short, but it is the one that makes the browser OAuth flow in Chapter 18 and the webhook handling in Chapter 22 possible.

Why state needs somewhere to live

When a user logs in, the proof of that login has to be stored somewhere the next request can find it. It cannot live in a Python variable, because the next request may be handled after the variable is gone. The browser cookie is the natural home: the server hands the browser a value, the browser sends it back on every later request, and the server reads it again. Flask wraps that exchange in the session object so you treat it like a dictionary.

The session object and secret_key

Writing to the session is dictionary assignment; reading it uses .get() with a default. Flask serialises the dictionary into a cookie, signs it with your secret_key, and sends it to the browser:

Use the same local-development pattern from Chapters 7 and 16: keep the real key in .env, call load_dotenv() when the app starts, then read the value with os.getenv().

.env
SECRET_KEY=replace-this-with-a-long-random-string
app.py
from flask import Flask, session, redirect, url_for
from dotenv import load_dotenv
import os

load_dotenv()

app = Flask(__name__)
# The signing key, required before any session write. The fallback
# keeps the example runnable; set SECRET_KEY for real deployments.
app.secret_key = os.getenv("SECRET_KEY", "dev-only-change-me")


@app.route("/set")
def set_name():
    session["name"] = "Ada"
    return redirect(url_for("greet"))


@app.route("/greet")
def greet():
    name = session.get("name", "stranger")
    return f"Hello, {name}!"

The signature is the important part. Flask does not encrypt the cookie; it signs it: the user can see the contents but cannot change them without the secret_key, so they cannot promote themselves by editing a cookie. That is why the key must be a real secret. Read it from .env locally or from your deployment platform's environment variables in production, never hardcode it in a file you commit, and use a long random value in production. The dev-only-change-me fallback keeps the example runnable while making it obvious it is not for real use.

Redirects with redirect() and url_for()

A redirect tells the browser to go and request a different URL. Pair it with url_for() so you redirect to a route by its function name rather than a hardcoded path:

app.py
@app.route("/old-home")
def old_home():
    return redirect(url_for("greet"))   # not redirect("/greet")

Naming the route means the redirect keeps working if you ever change the URL pattern, the same refactor-safety argument as static files on the previous page. This is also the second half of the Post/Redirect/Get pattern: process the POST, then redirect to a GET page.

One-time messages with flash()

After a redirect you often want to tell the user what just happened ("Note saved"). You cannot pass that message in a variable, because the redirect starts a fresh request. flash() stores the message in the session for exactly one request, then it is gone:

app.py
from flask import flash


@app.route("/save", methods=["POST"])
def save():
    # ... do the work ...
    flash("Note saved.")
    return redirect(url_for("note"))

The template displays whatever messages are waiting with get_flashed_messages(). Put this in your base layout so every page can show feedback:

templates/base.html
{% for message in get_flashed_messages() %}
  
{{ message }}
{% endfor %}

Why this page matters next

Sessions are not an academic detour. In Chapter 18, the browser OAuth flow stores the Spotify token in the session so the user stays logged in across pages, and it uses redirect() to send the user to Spotify and back. In Chapter 22, the same signing machinery underpins keeping webhook endpoints honest. Learn the mechanism here on a trivial example, and those later chapters become applications of something you already understand rather than new material.