HTTP, HTTPS, TLS & HTTP/2/3
How HTTP requests and responses work, how TLS secures HTTPS, and what HTTP/2 and HTTP/3 (QUIC) improve.
HTTP: the language of the web
HTTP (HyperText Transfer Protocol) is a request–response protocol. A client sends a request; the server returns a response. HTTP is stateless — each request stands alone, so state such as login sessions is carried in cookies or tokens.
An HTTP/1.1 request is plain text:
GET /tutorials/operating-systems HTTP/1.1
Host: www.noalabs.com
Accept: text/html
Accept-Encoding: gzip, br
Cookie: session=abc123
And a response:
HTTP/1.1 200 OK
Content-Type: text/html; charset=UTF-8
Cache-Control: public, max-age=300
Content-Length: 18342
<!doctype html>…
You can speak HTTP over a raw TCP socket yourself. This starts a tiny local web server and sends a hand-written request:
import socket
import threading
from http.server import HTTPServer, BaseHTTPRequestHandler
class Hello(BaseHTTPRequestHandler):
def do_GET(self):
body = b"Hello from the server"
self.send_response(200)
self.send_header("Content-Type", "text/plain")
self.send_header("Content-Length", str(len(body)))
self.end_headers()
self.wfile.write(body)
def log_message(self, *args): # keep the demo output clean
pass
server = HTTPServer(("127.0.0.1", 0), Hello)
threading.Thread(target=server.handle_request).start()
with socket.create_connection(server.server_address) as sock:
sock.sendall(b"GET /hello HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n")
response = b""
while chunk := sock.recv(4096):
response += chunk
head, body = response.split(b"\r\n\r\n", 1)
print(head.split(b"\r\n")[0].decode())
print(body.decode())
server.server_close()
Output:
HTTP/1.0 200 OK
Hello from the server
(Python's simple server replies with HTTP/1.0 by default — real servers like Nginx answer with HTTP/1.1 or newer.)
Methods and status codes
| Method | Purpose | Safe? | Idempotent? |
|---|---|---|---|
| GET | Read a resource | ✅ | ✅ |
| POST | Create / trigger processing | ❌ | ❌ |
| PUT | Replace a resource | ❌ | ✅ |
| PATCH | Partially update | ❌ | ❌ (usually) |
| DELETE | Remove | ❌ | ✅ |
| Class | Meaning | Common codes |
|---|---|---|
| 2xx | Success | 200 OK, 201 Created, 204 No Content |
| 3xx | Redirection / caching | 301, 302, 304 Not Modified |
| 4xx | Client error | 400, 401, 403, 404, 409, 422, 429 |
| 5xx | Server error | 500, 502 Bad Gateway, 503, 504 Gateway Timeout |
See the REST API tutorial for designing APIs with these.
HTTPS and TLS
Plain HTTP can be read and modified by anyone on the path (public Wi-Fi, a compromised router). HTTPS is HTTP inside TLS (Transport Layer Security), which provides:
- Confidentiality — traffic is encrypted.
- Integrity — tampering is detected.
- Authentication — the server proves its identity with a certificate.
The TLS 1.3 handshake (simplified)
Client Server
│ ── ClientHello: versions, ciphers, key share ──▶ │
│ ◀── ServerHello: chosen cipher, key share, │
│ certificate, signature, Finished ─────────── │
│ ── Finished ─────────────────────────────────────▶ │
│ ══════════ encrypted HTTP traffic ══════════════ │
- Both sides exchange ephemeral Diffie–Hellman key shares and derive the same session keys — without ever sending the key. Ephemeral keys give forward secrecy: stealing the server's private key later doesn't decrypt old traffic.
- The server sends its certificate and signs the handshake with its private key, proving it owns the certificate.
- Bulk data is then encrypted with fast symmetric ciphers such as AES-GCM or ChaCha20-Poly1305.
TLS 1.3 needs one round trip (TLS 1.2 needed two), and resumed sessions can use 0-RTT.
Certificates and the chain of trust
A certificate binds a domain name to a public key and is signed by a Certificate Authority (CA). Browsers and operating systems ship with trusted root CAs; the server sends its certificate plus intermediate certificates, forming a chain up to a trusted root. The client checks the chain, the expiry date and that the hostname matches. Free automated certificates from Let's Encrypt made HTTPS the default.
Python's default TLS settings enforce exactly these checks:
import ssl
ctx = ssl.create_default_context()
print("verify certificates:", ctx.verify_mode == ssl.CERT_REQUIRED)
print("check hostname: ", ctx.check_hostname)
print("minimum version: ", ctx.minimum_version.name)
Output:
verify certificates: True
check hostname: True
minimum version: TLSv1_2
Never disable verification (verify=False, CERT_NONE) in production code — it silently allows man-in-the-middle attacks.
HTTP/1.1 vs HTTP/2 vs HTTP/3
| HTTP/1.1 | HTTP/2 | HTTP/3 | |
|---|---|---|---|
| Transport | TCP | TCP | QUIC over UDP |
| Format | Text | Binary frames | Binary frames |
| Requests per connection | One at a time (browsers open ~6 connections) | Multiplexed streams on one connection | Multiplexed, independent streams |
| Header compression | No | HPACK | QPACK |
| Head-of-line blocking | At HTTP level | Fixed at HTTP level, still at TCP level | Eliminated (per-stream loss recovery) |
| Handshake | TCP + TLS | TCP + TLS | Combined QUIC + TLS 1.3 (1 RTT, 0-RTT resume) |
| Connection migration | No | No | Yes (survives Wi-Fi → mobile data switch) |
HTTP/2 lets many requests share one connection, so bundling tricks like domain sharding are no longer needed. HTTP/3 runs on QUIC, which moves reliability into user space over UDP: a lost packet only delays its own stream, and connections survive network changes — a big win on mobile networks.
Other useful headers
| Header | Purpose |
|---|---|
Strict-Transport-Security |
HSTS — force HTTPS for future visits |
Cache-Control, ETag |
Caching (see the Caching tutorial) |
Content-Encoding: br/gzip |
Compressed bodies |
Set-Cookie: …; Secure; HttpOnly; SameSite=Lax |
Safer cookies |
Access-Control-Allow-Origin |
CORS for cross-origin browser requests |
Content-Security-Policy |
Limits which scripts and resources can load |
Common mistakes
- Disabling certificate verification to "fix" an SSL error.
- Mixed content — loading HTTP scripts on an HTTPS page.
- Letting certificates expire — automate renewal and monitor expiry.
- Using GET for actions that change data — crawlers and prefetchers will trigger them.
Key takeaways
- HTTP is a stateless request–response protocol with methods, headers and status codes.
- HTTPS = HTTP over TLS: encryption, integrity and server authentication via certificates.
- TLS 1.3 uses ephemeral key exchange (forward secrecy) and a one-round-trip handshake.
- HTTP/2 multiplexes over TCP; HTTP/3 uses QUIC over UDP to remove head-of-line blocking.