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
- Set a cookie with response.set_cookie
- Read a cookie with request.cookies.get
- Control lifetime with max_age and expires
- Secure cookies with httponly, secure, samesite
- Delete a cookie with delete_cookie
- Know when to use sessions instead of cookies
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!
# True2 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 themeThe 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 cookie | Session | |
|---|---|---|
| Tamper-proof? | No — readable text | Yes — signed |
| Good for | Theme, language | Login state, flash messages |
| Needs SECRET_KEY? | No | Yes |
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 frCommon 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.
| Task | Code |
|---|---|
| Set a cookie | resp.set_cookie("k", "v") |
| Read a cookie | request.cookies.get("k") |
| Expire in 1 day | set_cookie("k","v", max_age=86400) |
| Delete a cookie | resp.delete_cookie("k") |
🏆 Mini Challenge: A Visit Counter
Use a cookie to count how many times this browser has visited.
- Read the current visits cookie (default 0)
- Increment it by one
- Save the new value back in a cookie and show the count
# 🏆 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
- Previous: URL Building & Redirects with url_for
- Next: Handling File Uploads — Accept file uploads safely with request.files and secure_filename
- Quick reference: Flask cheat sheet
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.