Working with Cookies

Reviewed & published by Brayan K

A cookie is a small piece of data your Flask app stores in the visitor's browser and reads back on later requests.

Part of the free Flask course at LearnCodingFast — hands-on lessons with worked examples and the output they print, plus practice exercises and a quick quiz.

In this lesson you'll set cookies on a response, read them from the request, control their lifetime and security flags, delete them, and learn when to reach for sessions instead.

What You'll Learn in This Lesson

1 Setting a Cookie

Cookies are attached to a response, so first build one with make_response(...), then call set_cookie("name", "value") on it. Flask adds a Set-Cookie header, and the browser stores it.

The test client behaves like a browser: it captures the Set-Cookie header and sends the cookie back on later requests. The example checks the header so you can see exactly what was sent.

# app.py — set a cookie on the response
from flask import Flask, make_response

app = Flask(__name__)

@app.route("/set-theme")
def set_theme():
    resp = make_response("Theme saved!")
    # set_cookie(name, value) attaches a Set-Cookie header
    resp.set_cookie("theme", "dark")
    return resp

# The test client captures the Set-Cookie header like a browser:
with app.test_client() as client:
    r = client.get("/set-theme")
    print(r.data.decode())                       # Theme saved!
    print("theme=dark" in r.headers["Set-Cookie"])   # True

# ✅ Expected output:
# Theme saved!
# True

2 Reading a Cookie Back

On the next request, the cookie arrives in request.cookies. Read it with request.cookies.get("name", default) — the default protects first-time visitors who don't have the cookie yet.

Because the test client remembers cookies across calls, the example below sets the cookie on one request and reads it on the next, exactly like a real browsing session.

# app.py — set on one request, read on the next
from flask import Flask, make_response, request

app = Flask(__name__)

@app.route("/set-theme")
def set_theme():
    resp = make_response("saved")
    resp.set_cookie("theme", "dark")
    return resp

@app.route("/page")
def page():
    # Read the cookie; fall back to 'light' for new visitors
    theme = request.cookies.get("theme", "light")
    return f"Showing the {theme} theme"

with app.test_client() as client:
    # First visit: no cookie yet -> default
    print(client.get("/page").data.decode())   # Showing the light theme
    # Save the preference, then visit again
    client.get("/set-theme")
    print(client.get("/page").data.decode())   # Showing the dark theme

# ✅ Expected output:
# Showing the light theme
# Showing the dark theme

The first visit shows the light default; after the cookie is set, the second visit reads dark — the browser sent it back automatically.

3 Lifetime, Security Flags & Deleting

By default a cookie disappears when the browser closes. Pass max_age (seconds) or expires (a datetime) to make it persist. Add security flags for anything sensitive: httponly=True (hidden from JavaScript), secure=True (HTTPS only), and samesite="Lax" (limits cross-site sending).

To remove a cookie, call delete_cookie("name") on a response — it sends an expired Set-Cookie so the browser drops it.

# app.py — lifetime, security flags, and deletion
from flask import Flask, make_response, request

app = Flask(__name__)

@app.route("/login")
def login():
    resp = make_response("Logged in")
    # Persist for 7 days and harden the cookie
    resp.set_cookie(
        "session_pref", "ok",
        max_age=60 * 60 * 24 * 7,   # one week in seconds
        httponly=True,
        samesite="Lax",
    )
    return resp

@app.route("/logout")
def logout():
    resp = make_response("Logged out")
    resp.delete_cookie("session_pref")   # tell the browser to drop it
    return resp

@app.route("/status")
def status():
    return "has cookie" if "session_pref" in request.cookies else "no cookie"

with app.test_client() as client:
    sc = client.get("/login").headers["Set-Cookie"]
    print("Max-Age=604800" in sc, "HttpOnly" in sc, "SameSite=Lax" in sc)  # True True True
    print(client.get("/status").data.decode())   # has cookie
    client.get("/logout")
    print(client.get("/status").data.decode())   # no cookie

# ✅ Expected output:
# True True True
# has cookie
# no cookie

🔬 Cookies vs. Sessions

Plain cookieSession
Tamper-proof?No — readable textYes — signed
Good forTheme, languageLogin state, flash messages
Needs SECRET_KEY?NoYes

Rule of thumb: non-sensitive preference → cookie. Anything a user shouldn't be able to forge → session.

🎯 Your Turn: Fill in the Blanks

Complete the routes so one sets a lang cookie and the other reads it. Replace each ___.

# 🎯 YOUR TURN: replace each ___ to set and read a cookie
from flask import Flask, make_response, request

app = Flask(__name__)

@app.route("/set-lang")
def set_lang():
    resp = ___("Language saved")        # build a response object
    resp.___("lang", "fr")              # set the cookie
    return resp

@app.route("/hello")
def hello():
    lang = request.___.get("lang", "en")   # read the cookie, default 'en'
    return f"Language is {lang}"

with app.test_client() as client:
    print(client.get("/hello").data.decode())   # Language is en
    client.get("/set-lang")
    print(client.get("/hello").data.decode())   # Language is fr

Common Errors & Quick Reference

❌ Trying to set_cookie on a string

A view that returns "hi" has no set_cookie method. Wrap it: resp = make_response("hi"); resp.set_cookie(...) and return resp.

❌ Storing secrets in a plain cookie

Plain cookies are readable and editable by the user. Never store user IDs you trust, passwords, or roles in one — use a signed session instead.

TaskCode
Set a cookieresp.set_cookie("k", "v")
Read a cookierequest.cookies.get("k")
Expire in 1 dayset_cookie("k","v", max_age=86400)
Delete a cookieresp.delete_cookie("k")

🏆 Mini Challenge: A Visit Counter

Use a cookie to count how many times this browser has visited.

# 🏆 Mini Challenge: a cookie-based visit counter
from flask import Flask, make_response, request

app = Flask(__name__)

@app.route("/")
def counter():
    # Cookies are strings, so convert to int
    visits = int(request.cookies.get("visits", "0")) + 1
    resp = make_response(f"Visit number {visits}")
    resp.set_cookie("visits", str(visits))
    return resp

with app.test_client() as client:
    print(client.get("/").data.decode())   # Visit number 1
    print(client.get("/").data.decode())   # Visit number 2
    print(client.get("/").data.decode())   # Visit number 3

❓ Frequently Asked Questions

Lesson complete — you can remember your visitors!

You can set cookies on a response, read them from request.cookies, control lifetime and security flags, delete cookies, and choose sessions when data must be tamper-proof.

Practice quiz

How do you attach a cookie to a Flask response?

  • return set_cookie('k', 'v')
  • request.set_cookie('k', 'v')
  • resp.set_cookie('k', 'v') on a make_response object
  • flask.cookie('k', 'v')

Answer: resp.set_cookie('k', 'v') on a make_response object. Build a response with make_response(...) then call resp.set_cookie('k', 'v').

How do you read a cookie named 'theme' in a view?

  • request.cookies.get('theme')
  • request.get_cookie('theme')
  • response.cookies['theme']
  • session.cookies('theme')

Answer: request.cookies.get('theme'). Incoming cookies live in request.cookies, a dict-like object; use .get for a safe default.

Why must you use make_response before set_cookie?

  • It encrypts the value
  • A plain string return has no set_cookie method
  • It is required by HTTP
  • It sets the SECRET_KEY

Answer: A plain string return has no set_cookie method. set_cookie is a method on a response object, so you need a real response, not a bare string.

Which argument sets how long a cookie persists, in seconds?

  • lifetime
  • timeout
  • duration
  • max_age

Answer: max_age. max_age takes a number of seconds; expires takes a datetime instead.

What does request.cookies.get('x', 'y') return when cookie 'x' is missing?

  • 'y'
  • None
  • an empty string
  • it raises KeyError

Answer: 'y'. The second argument is the default returned when the key is absent.

Which flag hides a cookie from client-side JavaScript?

  • secure=True
  • samesite='Strict'
  • httponly=True
  • signed=True

Answer: httponly=True. httponly=True blocks document.cookie access, reducing XSS theft.

What does secure=True do for a cookie?

  • Signs the cookie
  • Only sends it over HTTPS
  • Encrypts the value
  • Makes it permanent

Answer: Only sends it over HTTPS. secure=True tells the browser to send the cookie only on HTTPS connections.

How do you remove a cookie from the browser?

  • resp.delete_cookie('name')
  • request.cookies.pop('name')
  • resp.set_cookie('name', None)
  • del request.cookies['name']

Answer: resp.delete_cookie('name'). delete_cookie sends an expired Set-Cookie so the browser drops it.

Cookie values are stored in the browser as what type?

  • integers
  • JSON objects
  • strings
  • bytes

Answer: strings. Cookies are text, so numbers must be converted with int(...)/str(...).

For tamper-proof login state you should use what instead of a plain cookie?

  • a longer cookie
  • a Flask session
  • a query string
  • request.args

Answer: a Flask session. Sessions are signed (and need SECRET_KEY), so users cannot forge their contents.

Continue this course

Frequently asked questions

How do I set a cookie in Flask?

Build a response object with make_response(...), then call response.set_cookie('name', 'value'). The cookie travels back to the browser in a Set-Cookie header, and the browser sends it on every future request to your site until it expires.

How do I read a cookie in Flask?

Use request.cookies.get('name'). It behaves like a dictionary lookup and returns None (or a default you supply) when the cookie is absent, so request.cookies.get('theme', 'light') gives you 'light' for first-time visitors.

What is the difference between cookies and sessions?

A plain cookie stores data in the browser as readable text, so it is fine for non-sensitive preferences like a theme. A Flask session also uses a cookie, but the data is signed (and stored server-side in many setups) so it can't be tampered with — use sessions for anything security-related like login state.

What do httponly, secure, and samesite do?

httponly hides the cookie from JavaScript, blocking many XSS theft attacks. secure means the cookie is only sent over HTTPS. samesite controls whether the cookie is sent on cross-site requests, defending against CSRF. Set these on anything sensitive.