The session cookie is the classic web login method: on successful login the server issues a random session ID and sends it via `Set-Cookie`, and the browser then attaches it automatically as a `Cookie` header on every request. The proper design keeps the real user state (login info, permissions) in a server-side session store and puts only an identifier pointing to that store in the cookie.
Set-Cookie: sessionid=...; HttpOnly; Secure; SameSite=Lax on login → Cookie: sessionid=... on each requestThe flow: when the user logs in with credentials, the server creates a session, stores it (in memory, Redis, or a database), and sends its session ID as a cookie. The browser attaches the cookie on every same-domain request, so client code never has to manage a token by hand. Logout and forced expiry take effect instantly when the server deletes the session from the store, which is the revocation advantage that stateless JWTs lack.
Cookie security is governed by its attributes. HttpOnly blocks JavaScript's `document.cookie` access, reducing session theft via XSS; Secure restricts it to HTTPS; and SameSite (Lax/Strict/None) controls whether the cookie is attached on cross-site requests, greatly mitigating CSRF. Path, Domain, and Max-Age/Expires limit its scope and lifetime.
The very convenience of automatic cookie transmission is the root cause of CSRF (cross-site request forgery): if an attacker site makes the user's browser send a request to our server, the browser automatically includes the session cookie. That is why, alongside SameSite, you also use defenses such as CSRF tokens that require a separate verification value in the request.
Set-Cookie: sessionid=f3a1c9e2b7d84; Path=/; HttpOnly; Secure; SameSite=Lax; Max-Age=3600HTTP/1.1 200 OK
Set-Cookie: sessionid=f3a1c9e2b7d84; Path=/; HttpOnly; Secure; SameSite=Lax; Max-Age=3600fetch('/api/me', {
credentials: 'include' // sends the session cookie cross-site
});sid = request.cookies.get('sessionid')
session = store.get(sid) # server-side session store
if not session or session.expired:
return Response(status=401)
user = session.user