Sessions in Flask

Last updated June 4th, 2023

This article looks at how sessions work in Flask.

This article is part of a two-part series on how sessions can be used in Flask:

  1. Client-side: Sessions in Flask (this article!)
  2. Server-side: Server-side Sessions in Flask with Redis

This article assumes that you have prior experience with Flask. If you're interested in learning more about Flask, check out my course on how to build, test, and deploy a Flask application:

Developing Web Applications with Python and Flask

Contents

Sessions

Since HTTP is a stateless protocol, each request has no knowledge of any requests previously executed:

Flask Requests Responses Diagram

While this greatly simplifies client/server communication, web apps typically need a way to store data between each request as a user interacts with the app itself.

For example, on an e-commerce website, you'd typically store items that a user has added to their shopping cart to a database so that once they're done shopping they can view their cart to purchase the items. This workflow, of storing items in the database, only works for authenticated users, though. So, you need a way to store user-specific data for non-authenticated users between requests.

That's where sessions come into play.

A session is used to store information related to a user, across different requests, as they interact with a web app. So, in the above example, the shopping cart items would be added to a user's session.

The data stored for a session should be considered temporary data, as the session will eventually expire. To permanently store data, you need to utilize a database.

Computer storage is a nice analogy here: Temporary items on a computer are stored in RAM (Random Access Memory), much like sessions, while permanent items are stored on the hard drive, much like databases.

Examples of data to store in a session:

  • Items in a user's shopping cart
  • Whether the user is logged in or not
  • Preferences (language, currency, dark vs. light mode)

Examples of data to store in a database:

  • User credentials (email, username, hashed password, email confirmed boolean)
  • Data entered by the user (stock data, recipes, blog posts)

In Flask, you can store information specific to a user for the duration of a session. Saving data for use throughout a session allows the web app to keep data persistent over multiple requests -- i.e., as a user accesses different pages within a web app.

Sessions in Flask

There are two types of sessions commonly used in web development:

  1. Client-side - sessions are stored client-side in browser cookies
  2. Server-side - sessions are stored server-side (typically a session identifier is then created and stored client-side in browser cookies)

Cookies are small chunks of data stored on your computer by the web browser, with their original intent being to remember stateful information when browsing different websites.

Flask uses the client-side approach.

Pros:

  • Validating and creating sessions is fast (no data storage)
  • Easy to scale (no need to replicate session data across web servers)

Cons:

  • Sensitive data cannot be stored in session data, as it's stored on the web browser
  • Session data is limited by the size of the cookie (usually 4 KB)
  • Sessions cannot be immediately revoked by the Flask app

To store data across multiple requests, Flask utilizes cryptographically-signed cookies (stored on the web browser) to store the data for a session. This cookie is sent with each request to the Flask app on the server-side where it's decoded.

Since session data is stored in cookies that are cryptographically-signed (not encrypted!), sessions should NOT be used for storing any sensitive information. You should never include passwords or personal information in session data.

If you'd prefer to use server-side sessions, check out the Flask-Session package along with the Server-side Sessions in Flask with Redis article.

Session Example in Flask

The following app.py file illustrates how sessions work in Flask:

from flask import Flask, render_template_string, request, session, redirect, url_for


# Create the Flask application
app = Flask(__name__)

# Details on the Secret Key: https://flask.palletsprojects.com/en/2.3.x/config/#SECRET_KEY
# NOTE: The secret key is used to cryptographically-sign the cookies used for storing
#       the session data.
app.secret_key = 'BAD_SECRET_KEY'


@app.route('/set_email', methods=['GET', 'POST'])
def set_email():
    if request.method == 'POST':
        # Save the form data to the session object
        session['email'] = request.form['email_address']
        return redirect(url_for('get_email'))

    return """
        <form method="post">
            <label for="email">Enter your email address:</label>
            <input type="email" id="email" name="email_address" required />
            <button type="submit">Submit</button
        </form>
        """


@app.route('/get_email')
def get_email():
    return render_template_string("""
            {% if session['email'] %}
                <h1>Welcome {{ session['email'] }}!</h1>
            {% else %}
                <h1>Welcome! Please enter your email <a href="{{ url_for('set_email') }}">here.</a></h1>
            {% endif %}
        """)


@app.route('/delete_email')
def delete_email():
    # Clear the email stored in the session object
    session.pop('email', default=None)
    return '<h1>Session deleted!</h1>'


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

To run this example, start by creating and activating a new virtual environment:

$ mkdir flask-session
$ cd flask-session
$ python3 -m venv venv
$ source venv/bin/activate

Install Flask:

(venv)$ pip install Flask

Save the above code to an app.py file. Then, start the Flask development server:

(venv)$ python -m flask --app app --debug run

Now navigate to http://127.0.0.1:5000/get_email using your favorite web browser:

Flask Session Example - Step 1

Setting Session Data

In this example, the set_email view function processes the email when the form is submitted:

@app.route('/set_email', methods=['GET', 'POST'])
def set_email():
    if request.method == 'POST':
        # Save the form data to the session object
        session['email'] = request.form['email_address']
        return redirect(url_for('get_email'))

    return """
        <form method="post">
            <label for="email">Enter your email address:</label>
            <input type="email" id="email" name="email_address" required />
            <button type="submit">Submit</button
        </form>
        """

This view function supports the GET and POST HTTP methods. When the GET method is used, an HTML form is returned for you to enter your email address:

Flask Session Example - Step 2

When you submit the form with your email address (via the POST method), the email is saved in the session object:

# Save the form data to the session object
session['email'] = request.form['email_address']

Go ahead and enter your email at http://127.0.0.1:5000/set_email, and submit the form.

Accessing the Session Data

The get_email view function utilizes the Jinja templating engine to display either the email address stored in the session object or a link to the set_email() view function when an email is not stored in the session:

@app.route('/get_email')
def get_email():
    return render_template_string("""
            {% if session['email'] %}
                <h1>Welcome {{ session['email'] }}!</h1>
            {% else %}
                <h1>Welcome! Please enter your email <a href="{{ url_for('set_email') }}">here.</a></h1>
            {% endif %}
        """)

The session object is available for use within the template files!

When you navigate to the http://127.0.0.1:5000/get_email URL after entering your email address, your email will be displayed:

Flask Session Example - Step 3

Deleting the Session Data

The email address stored in the session object can be deleted via the delete_email view function:

@app.route('/delete_email')
def delete_email():
    # Clear the email stored in the session object
    session.pop('email', default=None)
    return '<h1>Session deleted!</h1>'

This view function pops the email element from the session object. The pop method will return the value popped, so it's a good practice to provide a default value in case the element is not defined in the session object.

When you navigates to the 'http://127.0.0.1:5000/delete_email URL, you will see:

Flask Session Example - Step 4

With the email address no longer stored in the session object, you'll once again be asked to enter your email address when you navigate to the http://127.0.0.1:5000/get_email URL:

Flask Session Example - Step 5

Session Uniqueness

To demonstrate how session data is unique to each user, enter your email address again at http://127.0.0.1:5000/set_email. Then, within a different browser (or a private/incognito window in your current browser) navigate to http://127.0.0.1:5000/set_email and enter a different email address. What do you expect to see after you're redirected to http://127.0.0.1:5000/get_email?

Since a different web browser is being used, this is considered a different user to the Flask app. Therefore, there will be a unique session utilized for that user.

Additional Notes

Cookies are intended to be small chunks of data (typically, 4KB).

If you run into unexpected problems when storing a large amount of data in the session object, check the size of the cookies in your responses vs. the size supported by the web browser. Since Flask serializes the data stored in the session object and stores it in a cookie, there could be a problem with the entire cookie not being saved.

Detecting Changes to Session Data

Based on the underlying data type (Werkzeug.CallbackDict) of the session object, it won't automatically detect changes to mutable data types (list, dictionary, set, etc.). Example:

session['shopping_cart'] = []
...
# Since a mutable data type (list) is being modified, this change
# is not automatically detected by the session object
session['shopping_cart'].append('bike')

# Therefore, mark the session object as modified
session.modified = True

Session Life

By default, the session object remains in place until the browser is closed. However, if you want to change the life of the session object, define the PERMANENT_SESSION_LIFETIME configuration variable after creating the Flask app:

import datetime

app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(minutes=1)

When setting the data in the session, specify that the sessions should be permanent (time will be based on PERMANENT_SESSION_LIFETIME):

# Save the form data to the session object
session['email'] = request.form['email_address']
session.permanent = True

Conclusion

This article showed how sessions work in Flask and looked at an example of storing a user's email address in the session object.

If you'd like to learn more about sessions in Flask, be sure to check out my course -- Developing Web Applications with Python and Flask.

Patrick Kennedy

Patrick Kennedy

Patrick is a software engineer from the San Francisco Bay Area with experience in C++, Python, and JavaScript. His favorite areas of teaching are Vue and Flask. In his free time, he enjoys spending time with his family and cooking.

Share this tutorial

Featured Course

Developing Web Applications with Python and Flask

This course focuses on teaching the fundamentals of Flask by building and testing a web application using Test-Driven Development (TDD).

Featured Course

Developing Web Applications with Python and Flask

This course focuses on teaching the fundamentals of Flask by building and testing a web application using Test-Driven Development (TDD).