C.W.K.
Stream
Lesson 06 of 07 · published

Headers by Category — A Taxonomy You Can Actually Hold

~11 min · foundations, headers, metadata, taxonomy

Level 0HTTP Newbie
0 XP0/46 lessons0/12 achievements
0/120 XP to next level120 XP to go0% complete
"There are hundreds of HTTP headers. You don't memorize them; you classify them. Once you know which category a header belongs to, you know roughly when to expect it and what it's doing."

Five Categories That Cover 95% of What You'll See

The IANA registry lists hundreds of headers, plus uncountable custom ones. Don't memorize the list. Memorize the categories — every header you meet falls into one of these five buckets, and the bucket tells you what it does:

  1. Request control — sent by the client to control how the server handles this request. Host, Authorization, Accept, Accept-Encoding, Accept-Language, User-Agent, Cookie, Origin, Referer.
  2. Response control — sent by the server about the response itself or future requests. Server, Set-Cookie, WWW-Authenticate, Location, Vary, Allow, Retry-After.
  3. Representation metadata — describe the body (request OR response). Content-Type, Content-Length, Content-Encoding, Content-Language, Last-Modified, ETag. These travel WITH the body.
  4. Caching & conditional — bridge request and response to coordinate caches. Cache-Control, Expires, ETag (yes, ETag plays in two categories), If-None-Match, If-Modified-Since, Vary. Track 5 lives here.
  5. Connection & transport — about the TCP connection rather than the message. Connection, Keep-Alive, Transfer-Encoding, Upgrade (used to switch to WebSocket). HTTP/2 and 3 drop most of these because the binary framing makes them unnecessary.

Reading a Real Exchange Through the Categories

Once the categories are in your head, a raw HTTP exchange decodes itself. Look at the request, classify every header, and you can predict what the server will do without reading any documentation. Same for the response — you know what's body-metadata, what's cache instructions, what's session state.

Headers are how clients and servers negotiate without touching the body. Content type, language, encoding, freshness, auth, session — all carried as headers so the body stays free for actual data. Trying to put any of this in the body (a JSON field called "requested_language") fights the protocol.

Custom Headers — Forget the X- Prefix

For decades, custom headers wore the X- prefix (X-Request-ID, X-Forwarded-For). RFC 6648 (2012) officially retired that convention. Today: pick any name you want, but make it descriptive (Pippa-Request-ID, Stripe-Signature, cf-ray). Many legacy X- headers are still in active use — X-Forwarded-For is everywhere — because removing them would break clients. Don't rename existing ones; just stop adding new X- ones to your own APIs.

cwkPippa's Header Reality

A typical cwkPippa request carries five headers from category 1: Host: localhost:8000, Authorization: Bearer ..., Accept: application/json, Content-Type: application/json (on POSTs), and User-Agent. Responses add representation metadata (Content-Type: application/json, Content-Length), connection control (Connection: keep-alive), and sometimes Cache-Control for static assets. SSE responses use Content-Type: text/event-stream plus Transfer-Encoding: chunked — the type tells the browser to use the EventSource parser; the encoding tells it the body has no fixed length.

Code

A real-world request, with every header categorized·http
GET /api/conversations HTTP/1.1                  <- request line
Host: localhost:8000                             <- request control
Authorization: Bearer eyJhbGciOi...              <- request control
Accept: application/json                         <- request control (negotiation)
Accept-Encoding: gzip, br                        <- request control (negotiation)
Accept-Language: ko-KR, en-US;q=0.8              <- request control (negotiation)
User-Agent: cwkPippa-WebUI/1.0                   <- request control
Origin: http://localhost:5173                    <- request control (CORS)
Cookie: session=abc123                           <- request control (session)
Pippa-Request-ID: req_xyz789                     <- request control (custom)
The matching response — five categories all present·http
HTTP/1.1 200 OK                                  <- status line
Content-Type: application/json; charset=utf-8    <- representation metadata
Content-Length: 4823                             <- representation metadata
Content-Encoding: gzip                           <- representation metadata
ETag: "v17-abc123"                              <- representation + caching
Last-Modified: Sun, 25 May 2026 03:00:00 GMT     <- representation + caching
Cache-Control: private, max-age=60               <- caching
Vary: Accept-Encoding, Authorization             <- caching + response control
Set-Cookie: session=abc123; HttpOnly; Secure     <- response control
Server: uvicorn                                  <- response control
Connection: keep-alive                           <- connection
Pippa-Request-ID: req_xyz789                     <- custom (echoed for tracing)
Programmatically classify every header on a response·python
import httpx

# Inspect every header on a real exchange
resp = httpx.get('https://creativeworksofknowledge.com/')

print('--- response headers, in arrival order ---')
for key, value in resp.headers.items():
    print(f'{key}: {value}')

# Categorize known ones
request_control = {'host', 'authorization', 'accept', 'accept-encoding',
                   'user-agent', 'cookie', 'origin', 'referer'}
representation  = {'content-type', 'content-length', 'content-encoding',
                   'content-language', 'last-modified', 'etag'}
caching         = {'cache-control', 'expires', 'if-none-match',
                   'if-modified-since', 'vary', 'age'}
response_ctrl   = {'server', 'set-cookie', 'www-authenticate', 'location',
                   'allow', 'retry-after'}
connection      = {'connection', 'keep-alive', 'transfer-encoding', 'upgrade'}

for key in resp.headers:
    lk = key.lower()
    if lk in representation: cat = 'representation'
    elif lk in caching:      cat = 'caching'
    elif lk in response_ctrl: cat = 'response-control'
    elif lk in connection:   cat = 'connection'
    else:                    cat = 'other/custom'
    print(f'{key:30s} -> {cat}')

External links

Exercise

Pick three different API responses you can hit easily: (1) a JSON API (cwkPippa, GitHub, Stripe), (2) a static asset (any image URL), (3) a streaming endpoint (an SSE source, like your own backend or any AI chat API). For each, list every response header and classify it into the five categories. Which categories does each kind of response lean on? Which headers ONLY appear in certain kinds of responses?
Hint
Static assets are caching-heavy (Cache-Control, ETag, Vary). JSON APIs lean on representation metadata + auth + custom tracing IDs. Streaming endpoints have unique markers: Content-Type: text/event-stream, Transfer-Encoding: chunked, often Cache-Control: no-cache. The pattern tells you what the endpoint is for before you read its docs.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.