hectoday
DocsCoursesChangelog GitHub
DocsCoursesChangelog GitHub

Access Required

Enter your access code to view courses.

Invalid code

← All courses HTTP from scratch

What is HTTP

  • The request-response model
  • Anatomy of an HTTP request
  • Anatomy of an HTTP response

Methods

  • GET and HEAD
  • POST
  • PUT, PATCH, and DELETE
  • OPTIONS and CORS preflight

Status codes

  • 2xx success
  • 3xx redirection
  • 4xx client errors
  • 5xx server errors

Headers

  • Request headers
  • Response headers
  • Custom headers

The body

  • JSON
  • Form data and multipart
  • No body

Connections

  • TCP, DNS, and TLS
  • HTTP/1.1 vs HTTP/2
  • Cookies and state

Putting it all together

  • Building a server from scratch
  • From scratch to framework

Cookies and state

HTTP has no memory

Here is something that might seem surprising after everything we have learned: HTTP is stateless. Every single request is independent. The server does not remember the previous request. Request 1 and request 2 might come from the same person, but as far as the server is concerned, they are strangers.

This is a fundamental property of the protocol. No memory. No context. No “logged in” state. Every request arrives as a blank slate.

The problem

Imagine a user logs in with POST /login. The server verifies their password. Great, they are authenticated. But then the user makes a second request: GET /orders. This arrives as a completely fresh, anonymous request. The server has no idea who sent it. The login from the previous request is gone.

Without some way to maintain state across requests, the user would have to send their username and password with every single request. That is impractical, insecure, and terrible for the user experience. So how do real applications solve this?

Cookies: giving the client a name tag

A cookie is a small piece of data that the server asks the browser to store. On every subsequent request to the same domain, the browser automatically sends that cookie back. This creates a persistent identity across requests.

Here is how it works, step by step:

Step 1: The server sets the cookie. After the user logs in, the server includes a Set-Cookie header in the response:

HTTP/1.1 200 OK
Set-Cookie: session=abc123; Path=/; HttpOnly; Secure; Max-Age=86400

This tells the browser: “Store session=abc123 and send it back to me on every request.”

Step 2: The browser sends the cookie. On the next request, the browser automatically includes it:

GET /orders HTTP/1.1
Host: api.example.com
Cookie: session=abc123

The browser did this on its own. The frontend code did not need to do anything.

Step 3: The server reads the cookie. When the request arrives, the server reads the session cookie, looks it up in the database, and knows who the user is:

route.get("/orders", {
  resolve: (c) => {
    const sessionId = c.request.headers.get("cookie")?.match(/session=([^;]+)/)?.[1];
    const session = db.prepare("SELECT * FROM sessions WHERE id = ?").get(sessionId);
    if (!session) return Response.json({ error: "Not authenticated" }, { status: 401 });
    // ... return orders for this user
  },
});

The server creates a session ID, stores it in a cookie, and uses it to identify the user on every subsequent request. HTTP itself is still stateless, but the application has built state on top of it using cookies.

[!NOTE] The Authentication course builds a complete session system using cookies. This lesson explains the underlying HTTP mechanism that makes it possible.

Cookie attributes

The Set-Cookie header can include attributes that control how the cookie behaves:

Max-Age=86400 means the cookie expires in 86,400 seconds (24 hours). After that, the browser deletes it. Without Max-Age, the cookie is a “session cookie” that gets deleted when the browser closes.

Path=/ means the cookie is sent on all paths. Path=/api would only send it on requests to /api/*.

Domain=example.com means the cookie is sent to example.com and all subdomains (api.example.com, www.example.com). Without Domain, the cookie is only sent to the exact host that set it.

HttpOnly means JavaScript cannot read the cookie via document.cookie. This protects against XSS attacks stealing session data.

Secure means the cookie is only sent over HTTPS. Never over unencrypted HTTP.

SameSite=Strict means the cookie is not sent on cross-origin requests. This protects against CSRF attacks.

Multiple cookies

A server can set multiple cookies with multiple Set-Cookie headers:

Set-Cookie: session=abc123; HttpOnly; Secure
Set-Cookie: theme=dark; Max-Age=31536000
Set-Cookie: language=en; Path=/

The browser sends all of them back in a single Cookie header:

Cookie: session=abc123; theme=dark; language=en

Deleting a cookie

To delete a cookie, set it with Max-Age=0:

Set-Cookie: session=deleted; Max-Age=0; Path=/

The browser sees Max-Age=0, removes the cookie immediately, and stops sending it.

That wraps up connections and state. We now understand what happens below HTTP (DNS, TCP, TLS), how connections evolved (HTTP/1.0 to HTTP/2), and how cookies add state to a stateless protocol. The final section puts all of this together by building a server from scratch.

Exercises

Exercise 1: Set a cookie with Set-Cookie. Make a follow-up request. Find the cookie in the Cookie request header.

Exercise 2: Set a cookie with Max-Age=10. Keep making requests for 15 seconds. Verify the cookie stops being sent after 10 seconds.

Exercise 3: Set a cookie with HttpOnly. Open the browser console and try document.cookie. Verify the cookie is not listed.

Why is HTTP called 'stateless' even though cookies exist?

← HTTP/1.1 vs HTTP/2 Building a server from scratch →

© 2026 hectoday. All rights reserved.