Flask Sessions: Track Users

Learn to use Flask sessions to track users in this Python web development tutorial—hands-on steps, troubleshooting, and what to study next.

Focus: use flask sessions to track users

Sponsored

Every time a user refreshes the page, logs in, or adds an item to a cart, your Flask app needs to know who they are — but HTTP is stateless. Without a mechanism to remember users across requests, every click feels like a first visit. Using Flask sessions to track users solves this by giving each visitor a unique, signed cookie that persists their data between requests. In this lesson, you'll move from anonymous views to a stateful, user-aware web app — no database required for the session itself.

The problem this lesson solves

Imagine a simple Flask app with a login route. When a user submits credentials, you validate them, and... then what? The next request comes in as a brand-new identity. HTTP doesn't remember anything by default. Without sessions, every page would ask for a password again — a terrible experience and a security nightmare.

The core pain: state. Web apps need to remember login status, preferences, shopping carts, or form data across multiple requests. This lesson solves that problem using Flask's built-in session object.

Why this matters now: In this track, you've built routes, templates, and forms. Sessions are the bridge that turns those isolated endpoints into a cohesive, interactive web application.

Core concept / mental model

Think of a session as a personal locker at a gym. The user gets a unique key (a cookie) when they enter. Every time they come back, they present that key, and the staff opens their locker with their gear inside. The gym doesn't store all lockers in the lobby — it stores them behind the counter, only accessible with the right key.

In Flask terms:

  • Session data is stored server-side (in a signed cookie by default) — like the locker's contents.
  • Session ID is embedded in the cookie — the key.
  • Flask signs the cookie with a secret key so no one can tamper with it — the locker's lock.

What exactly is a Flask session?

A session is a dict-like object that persists data across requests for the same client. Flask uses a cookie named session to store the data, signed with your SECRET_KEY. The client sends the cookie with every request, and Flask decodes it back into the session object.

Key definitions

  • Session: A temporary, user-specific storage that survives across requests.
  • Cookie: A small piece of data stored client-side, sent with each HTTP request.
  • Secret key: A random string used to cryptographically sign the session cookie for integrity.
  • Stateless: Each request is independent; the server doesn't remember previous ones.

How it works step by step

  1. Set a secret key in your Flask app — required before you can use sessions. Without it, Flask raises an error when you try to access the session.
  2. Import session from flask.
  3. Write to the session inside a view function — like session['username'] = 'alice'.
  4. Read from the session in any later request — username = session.get('username').
  5. Clear the session when the user logs out — session.clear().
  6. Set a session lifetime (optional) — for persistent logins or short-lived guest sessions.

Pro tip: The session is just a dict, but it behaves like one — you can use .get(), .pop(), in, and iterate over items. But remember: modifications must be assigned to be saved.

Hands-on walkthrough

Let's build a minimal Flask app that uses sessions to track a user's login status and name. We'll start with app setup, then add login/logout routes, and finally a protected profile page.

1. Basic setup with a secret key

# app.py
from flask import Flask, session, redirect, url_for, request, render_template_string

app = Flask(__name__
app.secret_key = 'change-me-in-production'

@app.route('/')
def index():
    return render_template_string('''
        <h1>Welcome</h1>
        <p>You are {{ 'logged in as ' + session['username'] if 'username' in session else 'not logged in' }}.</p>
        <a href="{{ url_for('login') }}">Login</a> | <a href="{{ url_for('logout') }}">Logout</a>
    ''')

if __name__ == '__main__':
    app.run(debug=True)

Run this and visit http://localhost:5000. You'll see "not logged in" — and if you open the browser's developer tools, you'll see a session cookie being set, even though it's empty.

2. Login and logout with sessions

# Add these routes to the same app.py
from flask import request, session, redirect, url_for, flash

@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        # In a real app, verify credentials against a database here
        username = request.form['username']
        # Dummy check: any username is 'valid' for this example
        session['username'] = username           # Store user in session
        session.permanent = True                  # Optional: persist across browser restarts
        return redirect(url_for('index'))
    return '''
        <form method="post">
            <input type="text" name="username" placeholder="Username" required>
            <button type="submit">Login</button>
        </form>
    '''

@app.route('/logout')
def logout():
    session.pop('username', None)  # Remove just the username
    return redirect(url_for('index'))

Test it: Go to /login, enter a name, submit. You'll be redirected to the homepage, now showing "logged in as ". Refresh the page — still logged in. Click "Logout" — back to anonymous.

3. Protecting a route with session checks

from functools import wraps
from flask import flash

def login_required(f):
    @wraps(f)
    def decorated(*args, **kwargs):
        if 'username' not in session:
            flash('Please log in to view that page.')
            return redirect(url_for('login'))
        return f(*args, **kwargs)
    return decorated

@app.route('/profile')
@login_required
def profile():
    return f"<h1>Profile</h1><p>Hello, {session['username']}! This is private.</p>"

Now hitting /profile without logging in redirects you to /login. After login, you can access it. This is a minimal but standard pattern.

Expected output

  • Visiting / without login: Welcome — You are not logged in.
  • After logging in as ada: Welcome — You are logged in as ada.
  • Accessing /profile before login: redirect to /login with a flash message.

Compare options / when to choose what

Flask sessions are great for simple, lightweight state. But they have limitations. Here's a comparison:

Approach Storage Use case Pros Cons
Client-side session (Flask default) Signed cookie (size limit ~4KB) Small data like user ID, preferences Simple, no server storage needed Data visible if not signed? No — signed. But size limited
Server-side session (e.g., Flask-Session) Redis, filesystem, DB Larger data, security-sensitive state Can store more, revocable Requires extra setup, network dependency
JWT (JSON Web Tokens) Client-side, stateless APIs, single-page apps Stateless, works across services Hard to revoke, size grows, security pitfalls
Database-backed session SQL/NoSQL High security, audit trails Durable, revocable, shareable More complex, slower

When to choose Flask's built-in session

  • Small data: user ID, username, theme preference — fits in a 4KB cookie.
  • Prototyping or simple apps: zero extra dependencies.
  • When security is medium-level: signed but not encrypted — don't store sensitive data like passwords or credit cards.

When to avoid it

  • Large data: cookies are sent with every request, so big sessions slow your app.
  • Sensitive data: the cookie is base64-encoded and signed, but readable by the client. Never store secrets.
  • High-scale or multi-server: client-side sessions are signed per-app; if you scale horizontally, each server needs the same secret key — use server-side sessions with shared storage instead.

Troubleshooting & edge cases

Common issues and fixes

  1. RuntimeError: The session is unavailable because no secret key was set. - Cause: You didn't set app.secret_key. - Fix: Add app.secret_key = os.urandom(24).hex() or a fixed string in config.

  2. Session changes not taking effect - Cause: Modifying nested values (like session['user']['name'] = ...) doesn't mark the session as modified. - Fix: Use session.modified = True after such changes, or reassign the whole dict item.

  3. session.permanent = True but session still expires - Cause: Default permanent lifetime is 31 days; you may want a custom value. - Fix: Set app.permanent_session_lifetime = timedelta(hours=8).

  4. Cookie size exceeded (usually with large data) - Cause: Cookies have a ~4KB limit; browsers refuse larger cookies. - Fix: Move session data to a server-side store using Flask-Session or store only a user ID in the session.

  5. Session data is readable by clients - Cause: Flask's cookie is signed but not encrypted. - Fix: Never store sensitive information. For encrypted server-side sessions, use Flask-Session with a secure backend.

Edge case: Multiple browser tabs

All tabs share the same cookie, so session changes in one tab affect all. That's usually desired, but be aware.

What you learned & what's next

You can now use Flask sessions to track users across requests. You understand the mental model of signed cookies, can set and read session data, protect routes with decorators, compare sessions to alternative state management, and troubleshoot common pitfalls.

You've mastered: - The core concept behind sessions — per-user, persistent state across HTTP requests. - How to implement login/logout using session dict operations. - How to protect routes with custom decorators. - When to choose client-side sessions vs. server-side sessions or JWTs.

Next up: In the next lesson, you'll take user tracking further by building user authentication with Flask-Login — a library that manages user sessions, password hashing, and current_user globals. You'll see how the session pattern you learned here scales to full authentication.

Keep practicing — try adding a theme preference stored in the session, or a simple item counter to see state persistence in action.

Practice recap

Try it yourself: Build a small Flask app that counts how many times a user visits a page, storing the count in the session. Then add a 'reset' button that clears the session. Watch the session cookie in your browser's dev tools to see how the data changes. For a challenge, add a theme toggle (light/dark) that persists across refreshes.

Common mistakes

  • Forgetting to set app.secret_key before accessing session — Flask raises a RuntimeError immediately.
  • Modifying nested session keys (session['user']['name'] = 'x') without setting session.modified = True, causing silent data loss.
  • Storing sensitive data like passwords or credit cards in session — the cookie is signed but not encrypted, so it's readable by the client.
  • Assuming sessions persist across browser restarts by default — you must set session.permanent = True and configure permanent_session_lifetime.

Variations

  1. Use Flask-Session to store sessions server-side in Redis, filesystem, or a database — useful for large or sensitive data.
  2. Use JWT (JSON Web Tokens) for stateless authentication in APIs, where sessions are impractical and cross-service validation is needed.
  3. Use database-backed sessions with a custom session interface for high-security apps that need audit trails and revocability.

Real-world use cases

  • E-commerce sites: track shopping cart contents across browsing sessions without requiring a database for each guest.
  • Web apps with user login: store a user ID and role in the session to protect admin pages and personalize content.
  • Multi-step forms: persist temporary user input between pages (e.g., signup wizard) to avoid resubmission.

Key takeaways

  • Sessions solve HTTP statelessness by giving each user a signed cookie that stores small, non-sensitive data.
  • Always set app.secret_key — it's mandatory for signing and integrity verification.
  • Read and write sessions like a dict: session['key'] = value, session.get('key'), session.pop('key').
  • Use @login_required decorators to protect routes based on session presence — a clean way to gate access.
  • Flask's default client-side session has a ~4KB limit and is readable — consider server-side options for sensitive or large data.
  • Set session.permanent and configure permanent_session_lifetime to control login persistence.

Sponsored

Sponsored

Discussion

Questions, corrections, and tips help everyone reading this page.

0 comments

Add a comment

Shown publicly with your comment.

Be constructive · max 4,000 characters

No comments yet — start the thread.

Related tutorials, quizzes, and articles for this topic.