Networking
Cookies, CORS and streaming
How cookies carry state in the browser, why CORS preflights cost a round trip, how compression and chunked responses shape delivery, and when to pick server-sent events, WebSockets or gRPC.
Three browser behaviors shape how APIs get used: cookies follow every matching request, CORS blocks reads across origins unless the server allows them, and responses can stream instead of arriving all at once. Each one is a source of confusing bugs if you do not know the rules.
Concept
Before you start: an origin is the scheme, host and port of a URL, such as https://app.example.com. Two URLs with different origins are cross-origin, even if they share a parent domain.
Cookies
The server sets state with Set-Cookie, and the browser sends the cookie back on matching requests:
Set-Cookie: session=8f2k...; Path=/; Secure; HttpOnly; SameSite=Lax; Max-Age=2592000Securemeans HTTPS only.HttpOnlyhides the cookie from JavaScript, which blunts session theft through XSS.SameSite=Laxstops the cookie from being sent on most cross-site subrequests. This is the main modern defense against CSRF.
Every cookie rides on every matching request. A 4 KB cookie on your static-asset domain adds 4 KB of upload to every image request. Authentication details are covered in authentication and authorization.
CORS
Browsers enforce the same-origin policy. JavaScript on app.example.com cannot read responses from api.other.com unless that server opts in with CORS headers.
For simple requests, such as GET or POST with basic content types, the browser sends the request and checks Access-Control-Allow-Origin on the response. For anything else, such as PUT, a JSON content type or a custom header like Authorization, the browser first sends a preflight OPTIONS request:
The preflight costs a full extra round trip. Access-Control-Max-Age lets the browser cache the preflight result. CORS protects users' browsers. It is not server-side access control. curl ignores it entirely.
Compression
Clients send Accept-Encoding: gzip, br, zstd, and the server picks one and labels it with Content-Encoding. Text compresses well. A 500 KB JSON or JavaScript payload often shrinks to 60 to 100 KB with gzip. Brotli typically saves another 15 to 20% over gzip on text.
Do not compress formats that are already compressed, such as JPEG or video.
Streaming
When the server does not know the body length up front, HTTP/1.1 uses chunked transfer encoding (Transfer-Encoding: chunked). It sends the body in length-prefixed pieces. HTTP/2 and HTTP/3 do not need it, since DATA frames already mark the body's boundaries.
Server-Sent Events (Content-Type: text/event-stream) keep one response open and push data: ... lines as events happen. Most LLM chat APIs stream tokens this way. It is simpler than WebSockets when data flows only from server to client.
WebSockets start as an HTTP/1.1 request with Upgrade: websocket, then turn the connection into a full-duplex message channel. Use them when both sides push messages at low latency, such as chat, collaborative editing or multiplayer.
gRPC runs on HTTP/2, with streams for concurrent and bidirectional calls and Protobuf bodies (see serialization). Because it relies on HTTP/2 trailers, browsers cannot call it directly. That is why gRPC-Web and Connect exist.
For a comparison of these choices, see real-time delivery.
Common mistakes
- Expecting CORS to protect your API. Any client can send requests that ignore CORS. Use authentication on the server.
- Sending large cookies to static domains. Every image request carries them.
- Choosing WebSockets for one-way updates. SSE is simpler and passes through more proxies.
Check yourself
- Why does a PUT from a browser cost one more round trip than a GET to the same API? The browser sends an OPTIONS preflight first, which waits for a response before the PUT is sent.
- Why does
SameSite=Laxhelp against CSRF? The browser does not send the cookie on most cross-site requests, so a forged form on another site cannot use the user's session.
Key takeaways
- Cookies follow every matching request.
HttpOnly,SecureandSameSitelimit how they can be stolen or misused. - CORS is a browser rule for reading cross-origin responses. Non-simple requests add one preflight round trip.
- Text compresses well. Already-compressed formats do not.
- Pick the streaming protocol by who sends messages: SSE for server to client, WebSockets for both, gRPC for service-to-service calls.
Further learning
MDN: HTTP documents cookies, CORS and streaming headers in detail.
Revisit requests, methods and status codes for the vocabulary used throughout.
Related
Authentication and authorization
Authentication proves who you are, authorization decides what you can do, and service-to-service calls need their own identity. The map for the rest of this topic.
HTTP/1.1, HTTP/2 and HTTP/3
The protocol of the web. Separates HTTP's meaning (methods, status codes, caching) from its wire format, and shows how each version fixes the latency problem of the one before.
Real-time delivery
How a server pushes updates to clients that did not ask, from polling to WebSockets, and the gateway, pub/sub and reconnect design that serves millions of open connections. Start here for the real-time map.