HTTP and TLS¶
What You'll Learn¶
- The structure of an HTTP request and response, and what status codes really tell you
- The headers that matter for operations: caching, proxies, and host routing
- What HTTP/2 and HTTP/3 change
- How TLS protects traffic, how certificates are validated, and how to debug TLS failures
Anatomy of an HTTP Request¶
> GET /orders/42 HTTP/2
> Host: api.example.com
> User-Agent: curl/8.9.1
> Accept: */*
>
< HTTP/2 200
< content-type: application/json
< cache-control: no-store
< x-request-id: 7f1c2a9e
<
{"id": 42, "status": "shipped"}
The Host header tells the server which site you want. One IP address can serve many hostnames — reverse proxies route on it.
Methods¶
| Method | Purpose | Safe (no changes) | Idempotent (repeatable) |
|---|---|---|---|
GET |
Read a resource | Yes | Yes |
HEAD |
GET without the body |
Yes | Yes |
POST |
Create, or trigger an action | No | No |
PUT |
Replace a resource | No | Yes |
PATCH |
Partially update | No | Not necessarily |
DELETE |
Remove | No | Yes |
Idempotency matters for retries: load balancers, clients, and service meshes can safely retry idempotent requests, but retrying a POST can create a duplicate order.
Status Codes¶
| Range | Meaning | Operational reading |
|---|---|---|
2xx |
Success | 200 OK, 201 Created, 204 No Content |
3xx |
Redirect | 301 permanent, 302/307 temporary, 304 Not Modified (cache hit) |
4xx |
The client's request was wrong | 400 bad input, 401 not authenticated, 403 not allowed, 404 not found, 429 rate limited |
5xx |
The server or something in front of it failed | 500 app error, 502 bad gateway, 503 unavailable, 504 gateway timeout |
For SLOs, 5xx responses are usually counted as errors and most 4xx are not — a user mistyping a URL isn't an outage. A sudden spike in 401, 403, or 429 still deserves an alert. Load Balancers explains what 502, 503, and 504 mean when a proxy is involved.
Headers That Matter in Operations¶
| Header | Why you care |
|---|---|
Host |
Virtual-host routing in proxies and ingress controllers |
X-Forwarded-For, X-Forwarded-Proto |
The original client IP and scheme after a proxy |
Cache-Control, ETag, Age |
Whether a CDN or browser serves a cached copy |
Strict-Transport-Security |
Forces HTTPS for future visits (HSTS) |
Retry-After |
How long a client should wait after 429 or 503 |
X-Request-Id / traceparent |
Correlates one request across logs and traces |
Connection: keep-alive |
Reuse TCP connections (default in HTTP/1.1) |
HTTP/1.1, HTTP/2, and HTTP/3¶
| HTTP/1.1 | HTTP/2 | HTTP/3 | |
|---|---|---|---|
| Transport | TCP | TCP | QUIC over UDP |
| Requests per connection | One at a time | Many, multiplexed | Many, multiplexed |
| Header format | Text | Binary, compressed | Binary, compressed |
| Head-of-line blocking | Per connection | At the TCP layer | Avoided — a lost packet delays only its own stream |
| TLS | Optional | Effectively required by browsers | Always built in |
HTTP/3 needs UDP port 443 open. If a firewall blocks it, clients fall back to HTTP/2 over TCP — slower to connect but it works.
gRPC runs over HTTP/2, so any proxy in front of a gRPC service must support HTTP/2 end to end.
Timing a Request¶
curl -s -o /dev/null -w '
dns: %{time_namelookup}s
connect: %{time_connect}s
tls: %{time_appconnect}s
first byte: %{time_starttransfer}s
total: %{time_total}s
status: %{http_code}
' https://api.example.com/health
Each value is cumulative from the start. A large jump between tls and first byte means the server is slow to respond; a large dns value points at resolution; a large connect points at network latency or a SYN queue problem.
TLS¶
TLS gives three guarantees: confidentiality (encryption), integrity (tampering is detected), and authentication (you're talking to the real api.example.com).
The TLS 1.3 handshake¶
sequenceDiagram
participant C as Client
participant S as Server
C->>S: ClientHello (SNI: api.example.com, key share, supported ciphers)
S-->>C: ServerHello (key share) + certificate + signature + Finished
Note over C: Verify the certificate chain and hostname
C->>S: Finished
Note over C,S: Encrypted application data (one round trip total)
- SNI (server name indication) sends the hostname in the first message, so one IP can serve certificates for many domains.
- TLS 1.3 completes in one round trip. TLS 1.0 and 1.1 are obsolete; TLS 1.2 is still widely supported.
How a certificate is validated¶
flowchart BT
Leaf["Leaf certificate<br/>api.example.com"] -->|"signed by"| Int["Intermediate CA"]
Int -->|"signed by"| Root["Root CA<br/>(in the client's trust store)"]
The client checks that:
- The chain leads to a root CA it already trusts.
- The hostname matches a Subject Alternative Name in the leaf certificate (the old Common Name field is ignored by modern clients).
- The current time is within the certificate's validity period.
- The certificate hasn't been revoked.
The server must send the intermediate certificates. Browsers sometimes fetch missing intermediates on their own, which hides the problem — while curl, Java, and Python clients fail.
Inspect a certificate¶
# What the server presents, including the chain
openssl s_client -connect api.example.com:443 -servername api.example.com -showcerts </dev/null
# Subject, issuer, SANs, and expiry of the leaf
openssl s_client -connect api.example.com:443 -servername api.example.com </dev/null 2>/dev/null \
| openssl x509 -noout -subject -issuer -ext subjectAltName -dates
# Days until expiry, for a monitoring check
end=$(openssl s_client -connect api.example.com:443 -servername api.example.com </dev/null 2>/dev/null \
| openssl x509 -noout -enddate | cut -d= -f2)
echo $(( ( $(date -d "$end" +%s) - $(date +%s) ) / 86400 )) days left
# A certificate file on disk
openssl x509 -in /etc/ssl/certs/app.pem -noout -text | less
Common TLS Errors¶
| Error | Cause | Fix |
|---|---|---|
certificate has expired |
Renewal failed or was never automated | Automate renewal (ACME, cert-manager, a cloud certificate manager) and alert 14+ days before expiry |
hostname mismatch / no alternative certificate subject name matches |
The requested name isn't in the SANs | Reissue with the right SANs, or fix the name clients use |
unable to get local issuer certificate |
Missing intermediate in the chain | Serve the full chain (fullchain.pem) |
self-signed certificate in certificate chain |
A private CA or a TLS-inspecting proxy the client doesn't trust | Add the CA to the trust store deliberately — never disable verification |
certificate is not yet valid |
Clock skew on the client or server | Fix NTP (timedatectl) |
wrong version number |
Speaking TLS to a plain HTTP port, or the reverse | Check the port and scheme |
handshake failure |
No shared protocol version or cipher | Update the client, or review server TLS settings |
Never ship curl -k or verify=False
Disabling certificate verification removes authentication entirely — any machine in the path can impersonate the server. Fix the trust problem instead.
Certificate Lifetimes and Automation¶
Public TLS certificates are short-lived and getting shorter: browser and CA industry rules are steadily reducing maximum validity, and Let's Encrypt certificates already last 90 days. Manual renewal no longer works at any scale. Use:
- ACME clients such as Certbot for servers
- cert-manager in Kubernetes
- Managed certificates from cloud load balancers (AWS Certificate Manager, Google-managed certificates), which renew automatically
Mutual TLS¶
In mTLS, the client also presents a certificate and the server verifies it. It's used for service-to-service authentication inside meshes (Istio, Linkerd) and for machine clients of sensitive APIs. The same chain and expiry rules apply in both directions.
Common Mistakes¶
- Serving only the leaf certificate, so non-browser clients fail with "unable to get local issuer certificate".
- Manually renewed certificates with no expiry monitoring — still one of the most common causes of full outages.
- Retrying non-idempotent
POSTrequests at the load balancer or client, creating duplicates. - Counting every
4xxas an error in SLOs, or ignoring a spike in401s after a deploy. - Using
curl -kin scripts and health checks, hiding real certificate problems. - Terminating TLS at a load balancer and sending plaintext across networks you don't control.
Interview Questions¶
- What's the difference between
502,503, and504? - Which HTTP methods are idempotent, and why does that matter for retries?
- Walk through a TLS 1.3 handshake. What is SNI for?
- A Python client fails with "unable to get local issuer certificate", but the site works in a browser. Why?
- How would you make sure certificates never expire unnoticed across hundreds of services?
Next¶
Continue to Load Balancers and Reverse Proxies.