JOVANA
Explore Library Glossary Getting Started Three Levels Fields How it works Mission
Join the mission
All guides

Cookies, State, and Web APIs (REST)

HTTP forgets you the instant it answers. So how does a website keep you logged in across a hundred clicks? This guide is about the small ticket called a cookie that puts memory back on top of a forgetful protocol, and about REST, the simple style that turns HTTP into a clean programming interface for machines.

A protocol with no memory

In the last guide you saw HTTP as a request-and-reply conversation: the browser asks with a method and a path, the server answers with a status code and a body, and then it is over. Here is the part that surprises most beginners. HTTP is stateless: each request stands completely on its own, and the server keeps no memory of what you asked a second ago. The next request arrives as if from a total stranger. This is by design, not by accident.

Why deliberately forgetful? Because forgetting scales. A stateless protocol means any server in a fleet can handle any request, since none of them is holding private notes about you. Crash a server and lose nothing; add ten more behind a load balancer and they are instantly interchangeable. The cost is obvious, though: a shopping cart, a logged-in session, a half-finished form, none of these survive a stateless world on their own. We need a way to carry a little memory across requests without giving up the freedom of forgetting.

The cookie: a small ticket the browser carries back

The fix is wonderfully simple, like a cloakroom ticket. The first time you visit, the server hands your browser a small token and says: hold onto this, and show it to me on every future request. That token is an HTTP cookie. The server sets it with a Set-Cookie response header; from then on the browser automatically attaches it in a Cookie request header on every request back to that site. Memory is reconstructed each time, not by the protocol, but by a value the browser faithfully echoes.

Picture the round-trip concretely. Your first request, say GET /login, carries no cookie. The server's reply adds a line like Set-Cookie: session=8fa3b1; HttpOnly; Secure; SameSite=Lax, planting the value in your browser. From then on, every later request, GET /cart, GET /orders, and so on, automatically includes Cookie: session=8fa3b1. The server reads 8fa3b1, looks it up, and thinks ah, this is Mia. The ticket went out once and comes back forever after.

Two flavours are worth telling apart. A session cookie usually carries only a random opaque id, like 8fa3b1, that means nothing by itself; the real data, your name, your cart, lives in the server's session store, and the id is just the key that looks it up. The other style stuffs the actual data into the cookie itself (often a signed token). The first keeps cookies tiny but needs server-side storage; the second is self-contained but the browser ships the whole thing on every request. It is the same old design tension: where do you keep the state, near the client or near the server?

Cookies are honest, not safe

Be clear-eyed about what a cookie is: a plain string the client stores and sends back, nothing more. By itself it proves nothing, hides nothing, and stops nothing. If the id is easy to guess, an attacker can guess it; if someone steals the cookie, they become you, because the server only ever sees the ticket, never the person holding it. A cookie is an honest courier, not a bodyguard.

That is why the small attributes in the Set-Cookie line matter so much. Secure tells the browser to send the cookie only over HTTPS, so it is never exposed on a plain connection that anyone on the wire could read. HttpOnly hides it from page JavaScript, blunting theft by malicious scripts. SameSite limits when the cookie rides along on requests started by other sites, which defends against a class of cross-site forgery. None of these makes the cookie secret on its own; they are guardrails layered on top of the one thing that actually provides confidentiality on the network, TLS, the same TLS that turns HTTP into HTTPS.

From web pages to web APIs: REST

So far HTTP has been delivering pages for a human to read. But the same protocol is brilliant at letting two programs talk, a phone app fetching your messages, a weather widget pulling a forecast. When HTTP is used as a clean interface for machines rather than browsers, we usually shape it in the style called REST (Representational State Transfer). REST is not a new protocol or a piece of software; it is a set of conventions for using plain old HTTP well.

The central idea is to model everything your service offers as a resource, and to give each resource an address, a URL. A user, an order, one photo, a whole list of photos: each is a thing with its own URL. Then, instead of inventing a hundred custom commands, you reuse HTTP's existing verbs, the request methods from the last guide, as the small fixed vocabulary of what you can do to any resource. The nouns are in the URL; the verb is the method.

Resource: a collection of orders, and one order inside it

  GET    /orders          -> read the list of orders        (200 OK)
  POST   /orders          -> create a new order             (201 Created)
  GET    /orders/42       -> read order #42                 (200 OK)
  PUT    /orders/42       -> replace order #42               (200 OK)
  DELETE /orders/42       -> remove order #42                (204 No Content)
  GET    /orders/999      -> there is no such order          (404 Not Found)

Nouns live in the URL; the verb is the HTTP method; the
outcome is an HTTP status code. No new protocol needed.
A small REST interface. The same four or five HTTP methods, pointed at resource URLs, cover create / read / update / delete, and the reply is an honest status code.

Two design choices make this pleasant to build on. First, REST leans on the same statelessness from the top of this guide: each API call carries everything the server needs (often including an auth token in an HTTP header), so the server need not remember the previous call. Second, the meaning of the result rides in the standard status code, so 200, 201, 404, and 500 mean the same thing to every client without anyone reading a custom manual. Reusing the web's existing vocabulary instead of inventing a private one is the whole win.

Putting it together: one logged-in request

Let us trace a single tap, you open your orders in a phone app, to see cookies (or tokens), statelessness, and REST all working at once. Watch how a forgetful protocol manages to act like it remembers you.

  1. Earlier, you logged in once. The server checked your password and planted a session id in a cookie (or handed the app a token). That single check is the only moment the server truly verified you.
  2. Now the app makes a REST call: GET /orders, with the cookie or an Authorization header riding along to say who is asking. Nouns in the URL, verb in the method.
  3. The server, holding no memory of you, reads the cookie's id, looks it up in its session store, and reconstructs in an instant: this is Mia. Statelessness restored, just for this one request.
  4. It returns 200 OK with your order list in the body, then promptly forgets you again. The next tap repeats the whole dance from scratch, which is exactly why any of the server's machines could have answered.

That is the quiet elegance of the application layer. HTTP stays simple and forgetful so it can scale; a tiny cookie or token rebuilds just enough memory, just in time; and REST borrows HTTP's own verbs and status codes so machines can talk without a new language. In the next guide we will see how the connection underneath this conversation evolved, from HTTP/1.1 to HTTP/2's multiplexing to HTTP/3 over QUIC, to make every one of these round-trips faster.