Skip to content
yutils

What's Actually Inside an HTTP Request

Open the wire — request line, headers, body, and the conversation patterns that hide behind GET and POST. Content negotiation, conditional requests, cookies, preflight, and the underrated headers everyone forgets.

~9 min read

Type a URL in the browser and a chunk of text flows across the wire — one request line + headers + blank line + optional body. It looks simple, but it hides content negotiation, conditional requests, cookies, preflight, and a stack of underrated headers. This guide opens the request, names the parts, and clarifies the conversations behind GET and POST.

One request on the wire

GET /api/users?page=2 HTTP/1.1
Host: example.com
Accept: application/json
User-Agent: Mozilla/5.0 (...)
Accept-Encoding: gzip, deflate, br
Cookie: session=abc123; theme=dark
Authorization: Bearer eyJhbGciOiJIUzI1Ni...

(blank line — no body)

A POST request adds a body:

POST /api/users HTTP/1.1
Host: example.com
Content-Type: application/json
Content-Length: 42
Authorization: Bearer eyJhbGciOiJIUzI1Ni...

{"name":"Alice","email":"a@b.c"}

Three parts:

  1. Request line — method + path + HTTP version
  2. Headers — name: value lines, terminated by a blank line
  3. Body — optional. Length set via Content-Length or chunked transfer

Methods — GET, POST, and the rest

MethodMeaningBodyIdempotentCacheable
GETRetrieve a resourceNoYesYes
POSTCreate resource / arbitrary actionYesNoNo
PUTReplace a resource fullyYesYesNo
PATCHModify part of a resourceYesImplementation-dependentNo
DELETEDelete a resourceOptionalYesNo
HEADGET's headers onlyNoYesYes
OPTIONSSupported methods / CORS preflightNoYesNo

Idempotent — calling N times has the same effect as calling once. That's the safety criterion for network retries.

Never use GET to mutate server state (HTTP/1.1 spec). Search crawlers will hit every GET URL — patterns like ?delete=1 cause real incidents.

Headers — too many to list, but here's the core

General / standard

  • Host — virtual hosting. Multiple domains on one IP. Required in HTTP/1.1.
  • User-Agent — client identifier (see the User-Agent Parser guide).
  • Accept — MIME types the client will take. application/json, text/html;q=0.9 (q = preference).
  • Accept-Language ko-KR, ko;q=0.9, en;q=0.8. Server picks the locale.
  • Accept-Encoding gzip, deflate, br, zstd. Server picks the compression.
  • AuthorizationBearer ... / Basic ... / Digest ....
  • Cookiename=value; name2=value2 (Cookie Parser).
  • Referer — previous page URL (typo from the original spec). Used for analytics and CSRF checks.
  • Origin — the requesting origin. Core of CORS.

Body-related

  • Content-Type — body MIME: application/json, multipart/form-data,application/x-www-form-urlencoded.
  • Content-Length — body byte count.
  • Transfer-Encoding: chunked — body size unknown ahead of time (streaming).
  • Content-Encoding — gzip / br / zstd applied to body (matching Accept-Encoding).

Conditional / caching

  • If-None-Match: "etag" — return 304 if unchanged
  • If-Modified-Since: Date — return 304 if unchanged since
  • Cache-Control: max-age=3600 — caching policy

Modern security / observability

  • Sec-CH-UA family — the User-Agent successor (Client Hints)
  • Sec-Fetch-* — request context (Mode / Dest / Site / User)
  • X-Request-ID — distributed tracing (Datadog, Honeycomb)

Content negotiation — why Accept-* matters

Client → Server:
GET /article/42
Accept: application/json, text/html;q=0.9
Accept-Language: ko-KR, ko;q=0.9
Accept-Encoding: gzip

Server picks:
- /article/42.json (Korean) if available and client prefers JSON
- Compresses with gzip
- Sets Vary: Accept, Accept-Language for cache awareness

q values (quality, 0.0-1.0) express preference. Omitted = 1.0. Server picks the highest-q acceptable representation.

Conditional GET — caching's secret sauce

First request:
GET /image.png
→ 200 OK
  ETag: "abc123"
  Cache-Control: max-age=86400

After browser cache expires:
GET /image.png
If-None-Match: "abc123"
→ 304 Not Modified  (zero-byte body!)

304 = "what you have is still current." Saves bandwidth and speeds up page loads. Core CDN mechanic.

POST body — four common encodings

application/json

POST /api/users HTTP/1.1
Content-Type: application/json

{"name":"Alice","age":30}

application/x-www-form-urlencoded

POST /login HTTP/1.1
Content-Type: application/x-www-form-urlencoded

name=Alice&password=secret&remember=true

HTML form default. URL-encoded (non-ASCII becomes %xx).

multipart/form-data

POST /upload HTTP/1.1
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW

------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="user"

Alice
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="avatar"; filename="me.jpg"
Content-Type: image/jpeg

(binary image data)
------WebKitFormBoundary7MA4YWxkTrZu0gW--

For file uploads. The boundary separates parts.

raw

text/plain / application/xml / any binary. SOAP uses XML.

CORS preflight — what OPTIONS actually does

Cross-origin browser requests with custom headers or non-simple methods trigger a preflight before the real request:

1. Preflight:
OPTIONS /api/data HTTP/1.1
Origin: https://app.example.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: x-custom-header

2. Server response:
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: x-custom-header
Access-Control-Max-Age: 86400

3. Real request (after preflight succeeds):
PUT /api/data HTTP/1.1
Origin: https://app.example.com
x-custom-header: value
...

See the cors-explained guide for the full story. Quick rules:

  • Simple requests (GET/HEAD with standard headers) skip preflight
  • Custom headers or PUT/DELETE/PATCH require preflight
  • Max-Age caches the preflight result (usually a day)

HTTP/1.1 vs HTTP/2 vs HTTP/3

  • HTTP/1.1 (1997) — text-based, one request per connection (Keep-Alive enables sequential pipelining). This guide's wire format.
  • HTTP/2 (2015) — binary frames, multiplexing (many requests over one connection). Header compression (HPACK).
  • HTTP/3 (2022) — runs over QUIC (UDP). Integrated TLS. Lower connection-setup RTT. Robust to mobile packet loss.

Semantics (methods, headers, bodies) are identical across the three. Only the wire format changes. This guide uses 1.1 for clarity.

Seeing the wire with curl

curl -v https://example.com/api/data
# -v shows the wire trace
# > lines are the request
# < lines are the response

curl -X POST https://example.com/api/users \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer abc123" \
  -d '{"name":"Alice"}'

cURL Builder turns a URL + method + headers + body into a curl command — handy when sharing requests in tickets.

Common pitfalls

1. Missing Content-Type

POST with a body but no Content-Type → server rejects (415 Unsupported Media Type) or parses it wrong.

2. Cookie's SameSite / Secure missing

Without SameSite=None; Secure, cross-site requests don't carry cookies. Independent of CORS.

3. Authorization case sensitivity

Authorization: Bearer abc123  ← standard
Authorization: bearer abc123  ← some servers reject
authorization: Bearer abc123  ← header NAMES are case-insensitive

4. GET query string length

Safe range is 2,000-8,000 bytes. Some servers and proxies truncate beyond that. Large parameters belong in a POST body.

5. Server ignores Accept

Sent Accept: application/json but got HTML back → client parse error. Verify the server contract.

6. CORS that only checks Origin

An attacker on the same Origin's different path slips through. Origin + path + custom header in combination.

References

Summary

  • HTTP request = request line + headers + blank line + optional body. Text-based in HTTP/1.1.
  • Method properties differ — idempotent / cacheable. Never use GET to mutate state.
  • Content negotiation — Accept / Accept-Language / Accept-Encoding with q-value preference.
  • Conditional GET — ETag / If-None-Match → 304 Not Modified. The CDN superpower.
  • POST bodies use four common encodings — JSON, form-urlencoded, multipart, raw.
  • CORS preflight (OPTIONS) for anything beyond simple requests.
  • HTTP/2 and 3 keep semantics identical, only the wire changes.
  • Try it: cURL Builder / Cookie Parser / User-Agent Parser / HTTP Status Codes.
Back to guides