HTTP Cheatsheet
Request and Response
Use this HTTP reference while you build software engineering projects, review code, or refresh the syntax you reach for most.
Anatomy of a request
An HTTP/1.1 request is a start line, header fields, a blank line, then an optional body. Line endings are CRLF.
POST /api/orders?draft=true HTTP/1.1 Host: api.example.com User-Agent: curl/8.7.1 Accept: application/json Content-Type: application/json Content-Length: 38 Authorization: Bearer eyJhbGciOiJIUzI1NiIs... {"sku":"HU-100","qty":2,"currency":"usd"}
| Part | Value above | Rules |
|---|---|---|
| Method | POST | case sensitive, always uppercase |
| Request target | /api/orders?draft=true | origin form for normal requests |
| Version | HTTP/1.1 | absent in HTTP/2 and /3, which are binary |
Host | api.example.com | required in 1.1, becomes :authority in 2 and 3 |
| Headers | Name: value | field names are case insensitive |
| Blank line | CRLF CRLF | ends the header block |
| Body | the JSON | length set by Content-Length or chunked framing |
Request target forms: origin (/path?query, the normal case), absolute
(https://host/path, used to a proxy), authority (host:port, only for
CONNECT), and asterisk (*, only for OPTIONS).
Anatomy of a response
HTTP/1.1 201 Created Date: Tue, 15 Sep 2026 10:04:11 GMT Content-Type: application/json; charset=utf-8 Content-Length: 52 Location: /api/orders/8814 Cache-Control: no-store ETag: "9c1f2a" {"id":8814,"sku":"HU-100","qty":2,"status":"draft"}
The status line is version, a three-digit code, and a reason phrase. The reason phrase carries no meaning and HTTP/2 and /3 drop it entirely, so never parse it.
URL structure
https://user:pw@api.example.com:8443/v1/orders?limit=20&sort=-date#top
\___/ \_____/ \_____________/ \__/\________/ \________________/ \_/
scheme userinfo host port path query fragment| Component | Notes |
|---|---|
| scheme | http (port 80) or https (port 443), case insensitive |
| userinfo | deprecated, browsers strip it, treat it as a phishing signal |
| host | DNS name, IPv4, or a bracketed IPv6 literal [2001:db8::1] |
| port | omitted when it is the scheme default |
| path | sent to the server, case sensitive by convention |
| query | ?k=v&k2=v2, percent-encoded, repeated keys are allowed |
| fragment | never sent to the server, client side only |
Reserved characters must be percent-encoded inside a component: space is %20
(or + in a form body), & is %26, = is %3D, ? is %3F, # is %23,
/ is %2F, + is %2B, % is %25.
Message framing
The receiver has to know where the body ends. There are exactly three ways.
| Mechanism | Header | Used when |
|---|---|---|
| Fixed length | Content-Length: 512 | the size is known up front |
| Chunked | Transfer-Encoding: chunked | streaming, size unknown (HTTP/1.1 only) |
| Connection close | neither | HTTP/1.0 legacy, response ends at EOF |
HTTP/1.1 200 OK Transfer-Encoding: chunked 1a {"partial":"first chunk"} 10 {"more":"data"} 0
Each chunk is a hex length, CRLF, the bytes, CRLF. A zero-length chunk ends the body, optionally followed by trailer fields.
Sending both Content-Length and Transfer-Encoding: chunked is the source of
request smuggling, because a proxy and an origin may disagree on which one
wins. Compliant servers must reject such a message. HTTP/2 and /3 have explicit
frame lengths, so the ambiguity disappears.
Connection reuse and pipelining
HTTP/1.1 keeps the TCP connection open by default, so subsequent requests skip the handshake.
| Header | Effect |
|---|---|
Connection: keep-alive | reuse, the 1.1 default, only explicit in 1.0 |
Connection: close | close after this exchange |
Keep-Alive: timeout=5, max=100 | non-standard hint on idle time and count |
Pipelining (sending several requests before reading replies) is defined but effectively unused, because responses must return in order and one slow response blocks the rest. That head-of-line blocking is what HTTP/2 multiplexing was built to solve.
Idempotent requests may be safely retried on a connection the server closes.
POST may not, without an idempotency key.
Sending requests by hand
curl -v https://api.example.com/v1/orders curl -i -X POST https://api.example.com/v1/orders \ -H 'Content-Type: application/json' \ -d '{"sku":"HU-100","qty":2}' curl -sI https://example.com # headers only, HEAD request curl -s -o /dev/null -w '%{http_code} %{time_total}\n' https://example.com curl -s --http1.1 -D - https://example.com -o /dev/null # dump headers curl --data-urlencode 'q=hack university' https://example.com/search curl -F file=@report.pdf -F title=Q3 https://example.com/upload curl -L https://example.com # follow redirects curl -k https://localhost:8443 # skip cert verification, dev only
# raw HTTP over a plaintext port printf 'GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n\r\n' \ | nc example.com 80 # raw HTTP over TLS printf 'GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n\r\n' \ | openssl s_client -quiet -connect example.com:443 -servername example.com
| Useful curl flag | Does |
|---|---|
-i | include response headers in the output |
-I | HEAD only |
-v / --trace-ascii - | handshake and header trace |
-L | follow Location redirects |
-H | add or override a request header |
-d / --data-binary | request body, implies POST |
-X | override the method |
--compressed | send Accept-Encoding and decode the reply |
--resolve host:port:ip | test one origin without touching DNS |
-w | print timing and metadata after the transfer |