The labs on this site need JavaScript to run. The same content is written out below.
Learn › Networking › HTTP
Cookies
Showing a name tag, every time, to a server that cannot remember you
HTTP does not link one request to the next. The server cannot tell whether an earlier request came from the same browser. So when a login succeeds, the server sends a small value (a cookie) in the response, and the browser stores it and attaches it automatically to every request that goes to the same site.
Partly checked against real execution · Checked with a browser and a test server (deletion when the browser is closed was not verified)
Concepts in this lab
- Cookie
- A small piece of data that a server sends in a response. The browser stores it and automatically sends it back with every request to the same site. HTTP does not remember state between requests, so this data is used to keep information that must carry over, such as a login state. Because it is stored in the user's browser, you must use it on the assumption that the user can read or change its contents.
- HTTP as a stateless protocol
- HTTP is a communication protocol in which a client sends a request and a server responds. Stateless means that the server does not remember the content of earlier requests, so each request must carry all the information needed to process it. Because the server keeps no state, any server can take a request, which makes it easy to add servers. Information that must carry over, such as a login state, is kept with cookies, sessions, or tokens.
- HttpOnly, Secure, and SameSite
- Attributes that protect cookies. HttpOnly stops JavaScript from reading the cookie, which prevents the cookie from being stolen by a script injection attack. Secure makes the browser send the cookie only over HTTPS connections. SameSite restricts sending the cookie with requests that start from another site, which reduces CSRF attacks.
- Session
- A way to store a user's state on the server side and give the client only an identifier (the session ID) that points to that stored state, delivered in a cookie. The actual information is on the server, so the client cannot change its contents (having the session ID stolen is a different problem). When there are several servers, a common setup keeps this state in a shared store such as Redis so that any server that receives a request can find the same state.
Try these
- Default settings — The cookie is attached automatically to every request to the same site.
- Without a cookie — Even for a request right after the login, the server does not know who sent it.
- Narrow the path to /shop — The cookie is not attached to a request for /orders.
- Turn off HttpOnly — JavaScript on the page can read the value of the cookie.
- Lifetime of 3s, request after 10s — The browser deletes an expired cookie.
- Close the browser and open it again — A cookie with no lifetime specified disappears. Change it to 1 hour and compare.
Running with the default settings
- This is the login request. It is the first request, so the browser has no cookie for this site. The server checks the ID and the password in the body.
- The server sends Set-Cookie: sid=abc123; Path=/; HttpOnly in the response header. The browser stores this value as a cookie of shop.example. The server writes down on its own side that "abc123 is member 7".
- This is the request for shop.example/orders. The browser attached sid to the Cookie header automatically. It is attached even though the code does not add it anywhere. The server looks up abc123 and knows that the request is from member 7.
- This is the request for shop.example/shop/cart. This path falls under Path=/, so the cookie is attached.
- This is a request going to another site, other.example. The cookie that shop.example gave is not attached. A cookie is attached only to requests going to the host that sent it, shop.example (the Domain attribute can widen this).
- JavaScript on the shop.example/shop/cart page reads document.cookie. A cookie with HttpOnly is not visible to scripts. It is still attached to requests automatically.
Result of this run
- A cookie with no lifetime specified is deleted when the browser is closed. "Stay logged in" is a cookie whose lifetime is specified with Max-Age or Expires.
- A cookie is stored in the user's browser, so the user can see and change its contents. That is why you do not put a value such as the member number directly into a cookie, but an identifier that cannot be guessed (a session ID) or a signed token. This continues in the "Sessions and JWT" lab.
Code
@PostMapping("/login")
ResponseEntity<Void> login(@RequestBody LoginRequest req) {
Member m = memberService.check(req.id(), req.password());
String sid = sessionStore.create(m.getId()); // "abc123" → member 7
ResponseCookie cookie = ResponseCookie.from("sid", sid)
.path("/")
.httpOnly(true)
.build();
return ResponseEntity.ok()
.header(HttpHeaders.SET_COOKIE, cookie.toString())
.build();
}
// Response header: Set-Cookie: sid=abc123; Path=/; HttpOnly
Assumptions of this model
The paths that a cookie is attached to according to Path, the fact that an HttpOnly cookie is not visible in document.cookie, the disappearance of a cookie after its Max-Age has passed, and the fact that a cookie is not sent to an address on a different host are the same as the results checked with a real browser and a test server. That a session cookie is deleted when the browser is closed follows the standard behavior, and it was not checked by running it. A browser with the "restore previous tabs" feature turned on may bring session cookies back. The Secure, SameSite, and Domain attributes are not covered in this lab.
Previous lab: HTTP/1.1, 2, 3 · Next lab: HTTP caching and ETag