Skip to content

HTTP & TLS

By now a request has resolved a name to an IP via DNS and opened a reliable TCP connection via the stack. Now the two ends have to actually say something to each other. HTTP is the language they speak, and TLS is the envelope that keeps the conversation private and verifies who’s on the other end. Together they’re the https:// in every URL you’ve ever typed.

HTTP is gloriously simple at its core: the client sends a request, the server sends back a response. That’s it. It’s also stateless — each request stands alone, carrying everything the server needs to handle it (which is why cookies and tokens exist: to re-supply context the protocol itself forgets).

A raw request and response look like this:

REQUEST RESPONSE
GET /api/users HTTP/1.1 HTTP/1.1 200 OK
Host: example.com Content-Type: application/json
Accept: application/json Content-Length: 84
(no body for a GET) {"users": [...]}

A request is a method + path + headers (+ an optional body). A response is a status code + headers + a body. Plain text, human-readable — you can literally type HTTP by hand.

The method says what you want to do with the resource at that path:

MethodMeansSafe?Idempotent?
GETreadyesyes
POSTcreate / submitnono
PUTreplacenoyes
PATCHpartially updatenono
DELETEremovenoyes

Idempotent means doing it twice has the same effect as doing it once — and it matters enormously for DevOps, because the network is unreliable and you will retry failed requests. Retrying a GET or PUT is safe; blindly retrying a POST can create two orders. That single property shapes how you build retry logic, health checks, and load balancers.

The response’s first line carries a three-digit code grouped by the leading digit:

2xx success 200 OK 201 Created 204 No Content
3xx redirect 301 Moved Permanently 304 Not Modified
4xx YOU erred 400 Bad Request 401 Unauthorized 404 Not Found
5xx SERVER erred 500 Internal Error 502 Bad Gateway 503 Unavailable

Internalize the 4xx-vs-5xx split: 4xx is the client’s fault, 5xx is the server’s. It tells you which side to debug. The ones you’ll see most on call: 502 Bad Gateway and 503 Service Unavailable — classic load balancer symptoms meaning “I couldn’t reach a healthy backend.” A wall of 5xx is your app dying; a wall of 404s is usually a bad deploy or routing change.

Headers are key-value metadata riding alongside the body: Content-Type (what format the body is), Authorization (who you are), Cache-Control (how long this may be cached), Host (which site you want, since one IP can serve many). They’re where most of HTTP’s real power and most of its subtle bugs live.

One header-driven feature matters for performance: keep-alive. Recall the TCP handshake costs a full round trip. HTTP/1.1 keeps the connection open by default and reuses it for many requests, so you pay the handshake (and the TLS handshake below) once instead of per request. HTTP/2 goes further: multiple requests share one connection concurrently (multiplexing), with compressed headers, so a page needing 50 resources no longer queues them behind each other. For a DevOps engineer the takeaway is simply: connection reuse is a major lever, and your proxies and clients should have it on.

Plain HTTP is sent in the clear — anyone on the path (the coffee-shop Wi-Fi, the ISP, a compromised router) can read or alter it. TLS (Transport Layer Security) wraps the connection to provide three things:

  • Encryption — eavesdroppers see only ciphertext.
  • Integrity — tampering is detected.
  • Authentication — you can verify the server really is example.com, not an impostor.

That third one is the subtle, crucial part. Encryption to the wrong server is useless, so TLS leans on certificates.

A certificate is a signed document binding a domain name (example.com) to a public key. The signature comes from a Certificate Authority (CA) — an organization your operating system and browser already trust. The trust chain works like this:

Your browser trusts a set of ROOT CAs (shipped with the OS)
|
Root CA signs -> Intermediate CA -> signs -> example.com's cert
|
Browser walks the chain; if it ends at a trusted root, the cert is valid

So when you connect, the server presents its certificate, your client checks that a trusted CA signed it and that the name matches and that it hasn’t expired. Only then does it trust the encrypted channel. This is why you can’t just make up a certificate for google.com — no trusted CA will sign it for you. Let’s Encrypt made these free and automatable, which is why HTTPS is now universal.

The handshake that sets all this up:

Client Server
| ---- ClientHello -------------> | (here are the ciphers I support)
| <--- ServerHello + certificate - | (here's my cert; let's use this cipher)
| ---- verify cert, agree key ---> |
| ===== encrypted from here on === |

This costs extra round trips on top of the TCP handshake — another reason keep-alive matters, since you do it once per connection, not once per request.

Terminal window
curl -v https://example.com # watch the TLS handshake + request + response headers
curl -I https://example.com # HEAD request: just the status line and headers
curl -s -o /dev/null -w "%{http_code}\n" https://example.com # print only the status code
# Inspect the live certificate and its expiry date:
echo | openssl s_client -connect example.com:443 2>/dev/null | openssl x509 -noout -dates

Step back from the request/response mechanics and weigh the protocol pair the way you’d weigh any adopted dependency:

  • Why does it exist? HTTP exists to give both ends a common language — a stateless request (method + path + headers + body) and a numbered response — so any client can talk to any server. TLS exists because that language is sent in the clear, and anyone on the path can read or alter it.
  • What problem does it solve? HTTP’s statelessness makes every request self-contained and safely retryable (hence cookies/tokens to re-supply context); TLS solves three problems at once — encryption, integrity, and the crucial one, authentication, since encrypting to an impostor is useless. Certificates signed by a trusted CA bind a name to a key so you know it’s really example.com.
  • What are the trade-offs? Statelessness costs you re-sending context on every request; the TLS handshake costs extra round trips on top of TCP (two in TLS 1.2, one in 1.3) — which is why keep-alive and connection reuse matter. TLS 1.3’s 0-RTT resumption is faster still but replayable, so it’s only safe for idempotent requests.
  • When should I avoid it? Don’t blindly retry a non-idempotent verb — a re-sent POST can create two orders, whereas GET/PUT/DELETE are safe. And don’t reach for 0-RTT on anything that mutates state.
  • What breaks if I remove TLS? The channel becomes eavesdroppable and tamperable, and you lose the identity guarantee entirely. Remove automated rotation and a single expired certificate makes every client refuse the connection — a total, self-inflicted outage that a calendar reminder won’t prevent.
  1. Why is HTTP described as stateless, and what mechanisms exist to re-supply the context it forgets?
  2. What does “idempotent” mean, and why does it govern how you safely retry failed requests over an unreliable network?
  3. A user gets a 502. Whose fault is a 5xx versus a 4xx, and what does 502 specifically hint at?
  4. TLS provides encryption, integrity, and authentication. Explain why authentication (via certificates and CAs) is what makes the encryption actually worth having.
  5. Why is automated certificate rotation safer than a calendar reminder? What manual step does it remove, and what outage does it prevent?
Show answers
  1. Each request stands alone — the server keeps no memory of prior requests — so every request must carry everything needed to handle it. Cookies and tokens (often in the Authorization header) exist to re-supply the context the protocol itself forgets.
  2. Idempotent means doing the operation twice has the same effect as doing it once. It governs retries because the network is unreliable and you will re-send failed requests: retrying a GET or PUT is safe, but blindly retrying a POST can create two orders.
  3. A 5xx is the server’s fault, a 4xx is the client’s. A 502 Bad Gateway specifically hints that a load balancer / reverse proxy couldn’t reach a healthy backend.
  4. Encryption only protects the channel to whoever’s on the other end — if that’s an impostor, you’ve privately handed your data to the attacker. Authentication (the server proving its identity via a certificate a trusted CA signed) is what guarantees you’re encrypting to the real example.com, which is what makes the encryption worth anything.
  5. A calendar reminder depends on a human remembering and acting; miss it and every client refuses the connection — a total self-inflicted outage. Automated rotation (e.g. cert-manager, certbot) removes the human entirely by renewing and reinstalling certs before they expire, preventing the expired-cert outage.