Cookies trip up more scrapers than almost anything else. A page loads fine in your browser but comes back empty, or redirects to a login, when your script requests it — nine times out of ten the difference is cookies. Handling them correctly is what keeps a session alive across requests, preserves a login, and stops a site from treating every hit as a brand-new anonymous visitor. This guide explains the browser-server cookie workflow and shows how to manage cookies in web scraping with modern tools, from requests to curl to headless browsers.
What a cookie is
A cookie is a small piece of data a server asks the client to store and send back on future requests. It's how the otherwise stateless HTTP protocol "remembers" you between page loads — that you're logged in, what's in your cart, which region or currency you picked, or that you already dismissed the consent banner.
The flow is simple:
- Your client requests a page.
- The server's response includes one or more
Set-Cookieheaders. - The client stores those cookies.
- On every subsequent request to that site, the client sends them back in a
Cookieheader.
In a browser, steps 3 and 4 are automatic. In a scraper, you are responsible for that machinery — capturing what the server sets and replaying it on the next request. Get it right and the site behaves exactly as it does for a real visitor.
Anatomy of a Set-Cookie header
A single Set-Cookie header carries a name-value pair plus attributes that control its scope and lifetime:
Set-Cookie: session_id=8f3b9c...; Domain=.example.com; Path=/; Expires=Wed, 09 Jul 2026 10:00:00 GMT; Secure; HttpOnly; SameSite=Lax
| Attribute | What it does |
|---|---|
Domain |
Which hosts the cookie is sent to (a leading dot includes subdomains). |
Path |
The URL path prefix the cookie applies to. |
Expires / Max-Age |
When the cookie dies. Absent = a session cookie, gone when the browser closes. |
Secure |
Only sent over HTTPS. |
HttpOnly |
Hidden from JavaScript (document.cookie can't read it) — a security measure, not a scraping obstacle for HTTP clients. |
SameSite |
Controls whether the cookie is sent on cross-site requests (Strict, Lax, or None). |
Session cookies vs persistent cookies is the distinction that matters most for scraping. Session cookies (no expiry) usually hold the live login or anti-bot token and must be captured fresh each run. Persistent cookies (with an expiry) hold longer-lived preferences and can sometimes be reused across runs.
Why cookies matter for scraping
Ignore cookies and you'll hit a wall of subtle failures:
- Lost sessions. A login handshake sets a session cookie; drop it and the next request is anonymous again.
- Redirect loops. Some sites bounce you to a "set a cookie" page and back; without cookie storage you loop forever.
- Consent / region gates. Many sites won't serve content until a consent or region cookie exists.
- Anti-bot tokens. Protection layers set a clearance cookie after a JavaScript check; every later request needs it. This overlaps heavily with anti-scraping techniques — the cookie is often the proof you passed the check.
Handling cookies well is a core scraping skill; if you're just getting started, ground yourself with what is web scraping first.
Cookies in Python with requests.Session
In Python, never manage cookies by hand — use a Session. It keeps a cookie jar that automatically stores every Set-Cookie and replays the right cookies on each request, exactly like a browser.
import requests
session = requests.Session()
# First request: the server sets cookies, the session stores them
session.get("https://example.com/login-page")
# Log in; the session captures the session cookie from the response
session.post("https://example.com/login", data={
"username": "user",
"password": "secret",
})
# Subsequent requests automatically carry the session cookies
resp = session.get("https://example.com/account/orders")
print(resp.text)
# Inspect what you're holding
for c in session.cookies:
print(c.name, c.value, c.domain)
Need to set a cookie manually (say, a region or consent value you already know)?
session.cookies.set("region", "US", domain=".example.com")
The same pattern exists in every language's HTTP stack — a session/client object that persists a cookie jar. Reach for it instead of stitching Cookie headers together yourself.
Cookies in cURL: the cookie jar
If you scrape with curl (on the command line or via a library binding), the equivalent of a session is the cookie jar — a text file where cURL saves cookies and from which it reloads them. Two options control it:
-c/CURLOPT_COOKIEJAR— the file to write cookies to after the request.-b/CURLOPT_COOKIEFILE— the file to read cookies from before the request.
Point both at the same file and cURL handles the full store-and-replay cycle for you:
# First call: log in, save cookies to the jar
curl -c cookies.txt -d "username=user&password=secret" https://example.com/login
# Later calls: load the jar, stay logged in, and update it
curl -b cookies.txt -c cookies.txt https://example.com/account/orders
The same idea in PHP's cURL binding, the pattern that older scrapers relied on:
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
$jar = __DIR__ . '/cookie.txt';
curl_setopt($ch, CURLOPT_COOKIEJAR, $jar); // write cookies here
curl_setopt($ch, CURLOPT_COOKIEFILE, $jar); // read them back next time
$body = curl_exec($ch);
curl_close($ch);
On the first call cURL creates the file; on every following call it reads cookies from the same jar, sends them to the server, and writes back any new values the server returns. The jar uses the classic Netscape cookie-file format — one line per cookie with domain, path, expiry, name, and value — so you can inspect or hand-edit it if you need to. That single shared file is what makes multi-step cURL scrapes (login, then fetch) work seamlessly.
Cookies with headless browsers
When a site sets cookies via JavaScript or only after an anti-bot challenge, a plain HTTP client can't reproduce them. Drive a real browser instead. Playwright, Puppeteer, and Selenium manage cookies natively and, crucially, let you export the cookie state so you can reuse it.
A powerful pattern is to log in once with a browser, save the storage state, then hand those cookies to fast requests calls:
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch()
context = browser.new_context()
page = context.new_page()
page.goto("https://example.com/login")
page.fill("#username", "user")
page.fill("#password", "secret")
page.click("#submit")
# Persist the whole cookie/session state to disk
context.storage_state(path="state.json")
browser.close()
Reuse it later without logging in again:
context = browser.new_context(storage_state="state.json")
Handing browser cookies to requests
The best of both worlds: let the browser clear the JavaScript hurdle, then pull the cookies into a lightweight requests.Session for speed.
# cookies = context.cookies() from Playwright
for c in cookies:
session.cookies.set(c["name"], c["value"], domain=c["domain"])
resp = session.get("https://example.com/data")
Practical tips
- Use a session/jar, not raw headers. Let the library persist and replay cookies for you.
- Keep one IP per session. If you rotate proxies mid-session, a cookie tied to one IP may be invalidated. Hold a sticky IP for the life of a login.
- Refresh session cookies each run. Login and anti-bot cookies expire; don't hard-code stale ones.
- Watch for
SameSiteand cross-domain flows. Auth that spans an identity provider may set cookies on more than one domain — make sure your client follows them all. - Respect the site. Reusing a valid session is fine; harvesting other users' cookies is not. Scrape public data within the site's terms and the law.
Summary
Cookies are how the web keeps state, and handling them is what separates a scraper that maintains a session from one that gets logged out on every request. Capture what the server sets, replay it on every follow-up call, and let a session object or cookie jar do the bookkeeping. For JavaScript- or challenge-set cookies, borrow a headless browser to establish the session, then optionally pass the cookies to a faster client.
If sustaining authenticated sessions across large jobs isn't where you want to spend your time, scraping.pro can build and run the scraper for you as a done-for-you data extraction service, sessions and cookies handled end to end.