DEV Community

pathvector-dev
pathvector-dev

Posted on • Originally published at blog.pathvector.dev

Redirects and Cookies: How Stateless HTTP Fakes Having a Memory

Originally published at https://blog.pathvector.dev/protocol-lab-http-27/ — part of the free Protocol Lab series.

This post is part of Protocol Lab, a free, hands-on series for learning networking protocols by building and breaking them in a container lab. All the lab material — topologies, configs, and scripts — lives in the repo: github.com/pathvector-studio/protocol-lab.

In Lab #10 we read one HTTP request and one response, in the clear. This lab adds the two mechanisms that make the web feel stateful even though HTTP is not: redirects (the server sends the client somewhere else) and cookies (the server hands the client a token to send back on every future request).

Reading guide: rfc-notes/http-redirects-cookies.md

Prerequisite: HTTP Lab 10: One Exchange, Read in the Clear

Expected time: 40–55 minutes.

The Goal

You will drive both mechanisms with curl:

  • GET /old returns 302 Found with a Location: /new header — "go here instead".
  • With curl -L, the client follows the redirect and ends up at /new.
  • GET /new sends Set-Cookie: session=abc123 — the server hands out a token.
  • The client stores it and resends it, so GET /whoami shows the server got Cookie: session=abc123 back.

By the end, you should be able to label this table:

Request Response Effect
GET /old 302 Found, Location: /new client is told to go to /new
GET /new (after -L) 200, Set-Cookie: session=abc123 client stores the cookie
GET /whoami (with -b) 200, body echoes Cookie: session=abc123 server sees the cookie

What You Will Learn

  • What a 3xx redirect is, and the role of the Location header.
  • The difference between a client that follows redirects and one that does not.
  • What Set-Cookie and Cookie are, and how they create a "session" on top of stateless HTTP.
  • Why cookies are how logins, carts, and preferences persist across requests.
  • That the server is otherwise stateless — it only knows you by the cookie you send.

This lab does not cover:

  • The differences between 301/302/303/307/308 in depth (we use 302).
  • Cookie security attributes (Secure, HttpOnly, SameSite) beyond naming them.
  • Server-side session storage, JWTs, or OAuth.

Where to Read in the RFCs

RFC Section What to focus on
RFC 9110 15.4 The meaning of 3xx (Redirection) and the Location header
RFC 9110 10.2.2 How Location is used
RFC 6265 3 The syntax of the Set-Cookie and Cookie headers
RFC 6265 4.1, 5.2 Cookie attributes (Path, Secure, HttpOnly, and friends)

The Big Picture

One client, one server. The server is a small Python app.

client (10.0.0.1) ------ eth1/eth1 ------ server (10.0.0.2:8080)
  curl                                     /old    -> 302 Location: /new
                                           /new    -> 200 Set-Cookie: session=abc123
                                           /whoami -> echoes the Cookie back
Enter fullscreen mode Exit fullscreen mode
sequenceDiagram
  participant C as client (curl)
  participant S as server

  C->>S: GET /old
  S-->>C: 302 Found, Location: /new
  Note over C: -L: follow the redirect
  C->>S: GET /new
  S-->>C: 200, Set-Cookie: session=abc123
  Note over C: -c: store the cookie in a jar
  C->>S: GET /whoami  (Cookie: session=abc123)
  S-->>C: 200, "you sent Cookie: session=abc123"
Enter fullscreen mode Exit fullscreen mode

10.0.0.0/24 is a local, closed range.

Note: Everything here runs in local address space, so nothing in this lab touches the real internet.

What You Need

Recommended environment:

  • Linux / WSL2 / a Linux VM
  • Docker
  • containerlab

Images used:

  • nicolaka/netshoot:latest — bundles both curl and python3.

No additional images are required. The server is examples/http-27/server/app.py (standard library only).

Running the Lab

The quick path, which deploys, verifies, and tears down for you:

./scripts/labctl.sh run http-27
Enter fullscreen mode Exit fullscreen mode

Or step through it manually:

1. Move into the working directory

cd protocol-lab/examples/http-27
Enter fullscreen mode Exit fullscreen mode

2. Deploy and start the server

sudo containerlab deploy -t http-27.clab.yml
docker exec -d clab-http-27-server python3 /app/app.py
Enter fullscreen mode Exit fullscreen mode

3. Watch the redirect (without following, then following)

# don't follow: see the raw 302 and Location header
docker exec clab-http-27-client curl -sD - -o /dev/null http://10.0.0.2:8080/old
# follow: -L takes you all the way to /new
docker exec clab-http-27-client curl -sL -w "\n[final] %{url_effective}\n" http://10.0.0.2:8080/old
Enter fullscreen mode Exit fullscreen mode
HTTP/1.1 302 Found
Location: /new
...
[final] http://10.0.0.2:8080/new
Enter fullscreen mode Exit fullscreen mode

4. Watch the cookie round trip

# /new returns Set-Cookie; -c saves it to a cookie jar
docker exec clab-http-27-client curl -sD - -o /dev/null -c /tmp/jar.txt http://10.0.0.2:8080/new
# send the cookie to /whoami with -b; the server echoes it
docker exec clab-http-27-client curl -s -b /tmp/jar.txt http://10.0.0.2:8080/whoami
Enter fullscreen mode Exit fullscreen mode
Set-Cookie: session=abc123; Path=/
...
you sent Cookie: session=abc123
Enter fullscreen mode Exit fullscreen mode

Expected Output

  • GET /old: HTTP/1.1 302 Found with Location: /new.
  • The final URL after following with -L: .../new.
  • GET /new: Set-Cookie: session=abc123.
  • GET /whoami (with the cookie attached): the body contains Cookie: session=abc123.

Why It Works

HTTP is fundamentally stateless. Each request is independent, and the server remembers nothing about previous requests. Yet on the web, logins persist and shopping carts survive page loads — thanks to two mechanisms: redirects and cookies.

  • Redirects (3xx + Location). The server tells the client "not that URL — look over here instead." 302 Found means "temporarily somewhere else." The destination goes in the Location header, not the response body. The client (a browser, or curl -L) reads it and automatically re-requests the new URL. This is what powers post-login page transitions, http→https upgrades, and moved URLs.
  • Following is the client's job. The server only points at the destination; whether to actually go there is up to the client. curl does not follow by default (it shows you the raw 302); -L makes it follow. Browsers follow automatically.
  • Cookies (Set-Cookie / Cookie). The server attaches Set-Cookie: name=value to a response, handing the client a small token. The client stores it and, from then on, automatically attaches Cookie: name=value to every request to the same server. The server looks at that value and recognizes "this is the client from before." That is how a "session" is built on top of stateless HTTP.
  • The server only knows you by your cookie. There is no memory of "you" on the server side (the app in this lab stores nothing at all). The cookie the client sends is the only clue. That is why deleting your cookies logs you out — and why a stolen cookie lets someone impersonate you (hence the Secure/HttpOnly/SameSite attributes that protect it).

The key insight: HTTP is stateless, but by steering the client with redirects and giving it a token to send back with cookies, it can behave as if it has state.

Common Pitfalls

  • Thinking the server takes you to the redirect target. The server only supplies Location. Following it is the client's job (-L, or a browser).
  • Looking for the 302 destination in the body. The destination lives in the Location header, not the body.
  • Thinking the server remembers the cookie. The client does the remembering. The server just reads whatever cookie is sent.
  • Confusing -c and -b. -c saves to the cookie jar; -b sends from it. Browsers do both automatically.
  • Dismissing cookie attributes. Secure/HttpOnly/SameSite matter for security (this lab keeps things minimal).
  • The 3xx family. 301 (permanent), 302 (temporary), 307/308 (method-preserving), and more — pick by use case.

Cleanup

sudo containerlab destroy -t http-27.clab.yml --cleanup
Enter fullscreen mode Exit fullscreen mode

If you used labctl.sh run http-27, the script runs destroy for you at the end.

Check Your Understanding

  1. What does a server return in a 3xx redirect? Which header carries the destination?
  2. Who follows a redirect? What flag makes curl follow one?
  3. Set-Cookie and Cookie — who sends each one, and to whom?
  4. HTTP is stateless, so why can a "session" persist?
  5. How does the server identify you? What happens if you delete the cookie?
  6. What are the cookie attributes Secure / HttpOnly / SameSite for?

References

Verified Run Log (2026-07-07)

This lab has been confirmed reproducible on real hardware.

Environment:

  • Ubuntu 26.04 LTS (kernel 7.0.0-27-generic, x86_64)
  • Docker 29.1.3
  • containerlab 0.77.0
  • client / server: nicolaka/netshoot:latest (curl / python3; the server runs server/app.py)

Running PATH="/tmp/pl-shim:$PATH" ./scripts/labctl.sh run http-27 performed deploy → verify → destroy, and verification.json returned "status": "verified".

The redirect (302 + Location, then following it)

$ curl -sD - -o /dev/null http://10.0.0.2:8080/old
HTTP/1.1 302 Found
Location: /new

$ curl -sL -w "[final] %{url_effective}\n" http://10.0.0.2:8080/old
[final] http://10.0.0.2:8080/new
Enter fullscreen mode Exit fullscreen mode

GET /old returns 302 Found with Location: /new. curl -L follows it and lands on /new. The server only points at the destination — the client does the following.

The cookie round trip (Set-Cookie → Cookie)

$ curl -sD - -o /dev/null -c jar.txt http://10.0.0.2:8080/new
Set-Cookie: session=abc123; Path=/

$ curl -s -b jar.txt http://10.0.0.2:8080/whoami
you sent Cookie: session=abc123
Enter fullscreen mode Exit fullscreen mode

GET /new hands out Set-Cookie: session=abc123, and the client (-c) stores it. Sending it to GET /whoami with -b, the server echoes back that it received Cookie: session=abc123. HTTP is stateless, but by turning the cookie into a "token you must send back," the server can identify the client across requests — and that is all a session really is.

Cleanup

containerlab destroy -t http-27.clab.yml --cleanup
Enter fullscreen mode Exit fullscreen mode

That's redirects and cookies: the server points, the client follows, and a little token sent back on every request is the entire illusion of state on the web.

Explore the full Protocol Lab series here: github.com/pathvector-studio/protocol-lab. If these labs are useful to you, please ⭐ star the repo on GitHub — it genuinely helps others find the project.

Next up, we'll layer more onto HTTP — think caching, content negotiation, and what happens when TLS enters the picture.

Top comments (0)