π¨ HTTP Request/Response Cycle
Every website you've ever loaded came down to one pattern repeated billions of times a second: a client sends a request, a server sends a response. This lesson takes that exchange apart line by line so you'll never again be mystified by a 404, a header, or a "CORS error" in the console.
π― Learning Objectives
By the end of this lesson, you will be able to:
- Describe the anatomy of an HTTP request and an HTTP response
- Choose the right HTTP method and reason about safe and idempotent operations
- Interpret status codes across all five categories
- Explain what common headers do, including key security headers
- Trace the full journey from typing a URL to a rendered page
- Handle requests and responses in Node.js, Python, and PHP
Estimated Time: 35β45 minutes β’ Difficulty: BeginnerβIntermediate
Hands-on: Decode real HTTP messages and match requests to the correct responses.
In This Lesson
What Is HTTP?
HTTP (HyperText Transfer Protocol) is the set of rules that governs how clients and servers exchange messages on the web. It defines the exact shape of a request, the exact shape of a response, and the vocabulary β methods, status codes, headers β they use to understand each other.
Invented by Tim Berners-Lee in the early 1990s, HTTP has grown through several versions β HTTP/1.1 (the readable text-based workhorse), HTTP/2 (binary and multiplexed for speed), and HTTP/3 (built on QUIC for lower latency). The semantics you'll learn here β methods, status codes, headers β are the same across all of them.
One property matters more than any other: HTTP is stateless. The server remembers nothing between requests. Each message must carry everything the server needs to fulfil it β which is exactly why tokens and cookies exist.
The Mail System Analogy
HTTP maps neatly onto sending a letter through the post:
| Postal mail | HTTP |
|---|---|
| The envelope (address, instructions) | Request headers |
| The letter inside | Request body |
| The postal service | The internet's routing |
| The reply letter | Response body |
| "Delivered" / "No such address" | Status codes (200, 404β¦) |
Status codes are the delivery notifications: 200 OK is "delivered, here's the reply"; 301 is "the recipient moved, I forwarded it"; 404 is "no one at this address"; 500 is "the recipient got it but had a breakdown answering." Like the post, HTTP relies on everyone following the same agreed formats.
Anatomy of a Request
Every HTTP request has the same four parts, in order:
- Start line β the method, the path, and the HTTP version (e.g.
GET /api/products HTTP/1.1). - Headers β metadata:
Host,Accept,Authorization,Content-Type, and more. - Blank line β a single empty line marking the end of the headers.
- Body β the payload, present on requests that send data (POST, PUT, PATCH).
HTTP methods
The method declares your intent. Two properties matter: a safe method doesn't change server state, and an idempotent method has the same effect whether you send it once or ten times.
| Method | Purpose | Body | Safe | Idempotent |
|---|---|---|---|---|
| GET | Retrieve a resource | No | β | β |
| POST | Create a resource / submit data | Yes | β | β |
| PUT | Replace a resource entirely | Yes | β | β |
| PATCH | Partially update a resource | Yes | β | β |
| DELETE | Remove a resource | Sometimes | β | β |
| HEAD | Like GET, headers only | No | β | β |
| OPTIONS | Ask what's allowed (used by CORS) | No | β | β |
A sample POST that creates a user:
POST /api/users HTTP/1.1
Host: example.com
Content-Type: application/json
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
{
"name": "John Doe",
"email": "john.doe@example.com"
}
Anatomy of a Response
A response mirrors the request's structure β the only difference is the first line, which reports the outcome instead of stating a method.
- Status line β HTTP version, a status code, and a reason phrase (e.g.
HTTP/1.1 200 OK). - Headers β metadata about the response:
Content-Type,Content-Length,Cache-Control,Set-Cookieβ¦ - Blank line β separates headers from the body.
- Body β the actual content: HTML, JSON, an image, or nothing at all (as with
204 No Content).
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 165
Cache-Control: max-age=3600
{
"id": 42,
"name": "Wireless Headphones",
"price": 99.99,
"inStock": true,
"categories": ["electronics", "audio"]
}
π Reason phrase vs. status code
The code (200) is for machines; the reason phrase ("OK") is a human-readable hint. Clients act on the code β never parse the phrase, since servers may word it differently.
Status Codes
Status codes fall into five ranges. Learn the ranges first, then the handful of specific codes you'll meet daily.
| Range | Meaning | Mnemonic |
|---|---|---|
| 1xx | Informational | "Hold onβ¦" |
| 2xx | Success | "Here you go" |
| 3xx | Redirection | "Go look over there" |
| 4xx | Client error | "You messed up" |
| 5xx | Server error | "I messed up" |
The ones you'll actually use
- 200 OK β success (GET, PUT, PATCH).
- 201 Created β a POST created a new resource.
- 204 No Content β success with nothing to return (often DELETE).
- 301 / 302 β moved permanently / found (temporary redirect).
- 304 Not Modified β your cached copy is still good.
- 400 Bad Request β malformed or invalid input.
- 401 Unauthorized β you're not authenticated (log in).
- 403 Forbidden β authenticated, but not allowed.
- 404 Not Found β no such resource.
- 429 Too Many Requests β you've been rate-limited.
- 500 Internal Server Error β an unhandled server-side failure.
- 503 Service Unavailable β server temporarily overloaded or down.
β οΈ 401 vs. 403 β a classic mix-up
401 Unauthorized means "I don't know who you are" β authenticate and try again. 403 Forbidden means "I know exactly who you are, and you still can't do this." Using the wrong one confuses clients and leaks information.
Headers in Depth
Headers are the knobs and dials of an HTTP exchange. A few you'll reach for constantly:
- Content-Type β the format of the body (
application/json,text/html,multipart/form-data). - Accept β the formats the client can handle in the response.
- Authorization β credentials, usually
Bearer <token>. - Cache-Control β how (and whether) the response may be cached.
- Set-Cookie / Cookie β how servers and clients pass small state back and forth.
- Location β where to go on a 3xx redirect or after a 201 Created.
Security headers you should know
| Header | What it does |
|---|---|
Strict-Transport-Security | Forces browsers to use HTTPS for your site |
Content-Security-Policy | Restricts which sources of scripts/styles may load β a strong XSS defence |
X-Content-Type-Options: nosniff | Stops the browser from guessing (sniffing) content types |
Access-Control-Allow-Origin | The CORS header that decides which origins may call your API |
π‘ What "CORS error" really means
When your frontend on one origin calls an API on another, the browser first checks the API's Access-Control-Allow-Origin header. If your origin isn't allowed, the browser blocks the response β that's the dreaded CORS error. The fix is on the server: send the correct CORS headers.
The Full Cycle
Typing a URL and pressing Enter kicks off a chain of steps far longer than "send request, get response." Here's the whole journey.
- DNS resolution β translate the domain name into an IP address.
- Connection β a TCP handshake, and for HTTPS a TLS handshake to encrypt the channel.
- Request β the browser sends the HTTP request over that connection.
- Server processing β the server routes the request, runs logic, and queries data stores.
- Response β the server sends back a status code, headers, and a body.
- Client processing β the browser parses the response, renders it, and fires off new requests for CSS, JS, and images.
With HTTP/2 and HTTP/3, steps 3β5 can be multiplexed β many requests and responses share one connection at once, eliminating the "wait in line" delays of HTTP/1.1.
Handling HTTP in Code
Here's the same small API β list users and create a user β in each backend language, so you can see how the concepts map to real handlers. Notice how the pieces line up: read query params or the body, validate, then respond with the right status code.
Node.js (Express)
import express from 'express';
const app = express();
app.use(express.json());
// GET with query parameters
app.get('/api/users', (req, res) => {
const page = Number(req.query.page) || 1;
const limit = Number(req.query.limit) || 10;
res.status(200).json({
users: [{ id: 1, name: 'John' }, { id: 2, name: 'Jane' }],
page, limit, total: 100,
});
});
// POST with validation -> 201 Created
app.post('/api/users', (req, res) => {
const { name, email } = req.body;
if (!name || !email) {
return res.status(400).json({ error: 'Name and email are required' });
}
res.status(201).json({ id: 3, name, email });
});
app.listen(3000, () => console.log('Listening on http://localhost:3000'));
Python (Flask)
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route('/api/users', methods=['GET'])
def get_users():
page = request.args.get('page', 1, type=int)
limit = request.args.get('limit', 10, type=int)
return jsonify(
users=[{'id': 1, 'name': 'John'}, {'id': 2, 'name': 'Jane'}],
page=page, limit=limit, total=100
), 200
@app.route('/api/users', methods=['POST'])
def create_user():
data = request.get_json() or {}
name, email = data.get('name'), data.get('email')
if not name or not email:
return jsonify(error='Name and email are required'), 400
return jsonify(id=3, name=name, email=email), 201
if __name__ == '__main__':
app.run(port=3000)
PHP
<?php
header('Content-Type: application/json');
$method = $_SERVER['REQUEST_METHOD'];
$path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
if ($method === 'GET' && $path === '/api/users') {
$page = (int)($_GET['page'] ?? 1);
$limit = (int)($_GET['limit'] ?? 10);
http_response_code(200);
echo json_encode([
'users' => [['id' => 1, 'name' => 'John'], ['id' => 2, 'name' => 'Jane']],
'page' => $page, 'limit' => $limit, 'total' => 100,
]);
} elseif ($method === 'POST' && $path === '/api/users') {
$data = json_decode(file_get_contents('php://input'), true) ?? [];
$name = $data['name'] ?? null;
$email = $data['email'] ?? null;
if (!$name || !$email) {
http_response_code(400);
echo json_encode(['error' => 'Name and email are required']);
exit;
}
http_response_code(201);
echo json_encode(['id' => 3, 'name' => $name, 'email' => $email]);
} else {
http_response_code(404);
echo json_encode(['error' => 'Route not found']);
}
β Same concepts, three dialects
Read the request, validate the input, pick the correct status code, send a JSON response. Every backend framework you'll ever use is a variation on this theme.
Hands-on Exercise
ποΈ Decode & Match HTTP Messages
Objective: Read HTTP messages fluently and pick the right response for a request.
Part A β Analyse this request
POST /api/login HTTP/1.1
Host: example.com
Content-Type: application/json
{
"username": "john.doe",
"password": "secretpassword123"
}
Answer: (1) What operation is this? (2) What format is the body? (3) What's its purpose? (4) What one security issue jumps out?
Part B β Match the request to a response
For GET /api/products?category=electronics&sort=price, which response fits: a 200 OK with a product list, a 201 Created with a new product, or a 405 Method Not Allowed?
π‘ Hint
Match the method to the status class. A GET is a read β it should return existing data, not create anything. 201 is for creation (a POST); 405 is for a method the endpoint doesn't support.
β Solution
Part A: (1) A POST β submitting login credentials. (2) JSON. (3) Authenticate a user and start a session / issue a token. (4) The request must be sent over HTTPS; over plain HTTP the password travels in clear text. (Bonus: the server should never store that password in plaintext β hash it.)
Part B: The 200 OK with the filtered product list. A GET reads data, so 201 (creation) and 405 (method not allowed) are both wrong for a valid GET on a listing endpoint.
π― Quick Quiz
Question 1: A POST request successfully creates a new resource. Which status code should the server return?
Question 2: A user is logged in but tries to delete another user's post and is refused. Which status code is correct?
Question 3: What does it mean that an HTTP method is idempotent?
Summary & Quiz
π Key Takeaways
- HTTP is a stateless protocol; every request must carry everything the server needs.
- Requests and responses share the same shape: start/status line β headers β blank line β optional body.
- Pick the right method and know which are safe and idempotent.
- Learn status codes by range (2xx/3xx/4xx/5xx), then the daily specifics β and don't confuse 401 with 403.
- Headers control caching, auth, content types, CORS, and security.
- The full cycle runs DNS β connect β request β process β response β render.
π Further Reading
π What's Next?
You now know the shape of every HTTP message. Next we go deeper into the vocabulary itself in HTTP Methods, Headers, and Status Codes β sharpening your instinct for choosing exactly the right ones.
π You can read the wire now!
Requests, responses, methods, codes, headers β the whole conversation makes sense. This is the language every API speaks.