Skip to main content

πŸ“¨ 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.

sequenceDiagram participant Client participant Server Client->>+Server: HTTP request Note right of Server: Server processes it Server-->>-Client: HTTP response Note over Client,Server: The cycle, repeated endlessly

The Mail System Analogy

HTTP maps neatly onto sending a letter through the post:

Postal mailHTTP
The envelope (address, instructions)Request headers
The letter insideRequest body
The postal serviceThe internet's routing
The reply letterResponse 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:

The four parts of an HTTP request Stacked bars: a start line, headers, a blank line, and an optional body. Start line METHOD /path HTTP/1.1 Headers Host, Accept, Authorization, Content-Type … Name: Value (one per line) (blank line β€” separates headers from body) Body (optional) JSON, form data, file upload …
Figure 1 β€” The structure of an HTTP request. A GET request usually has no body; a POST or PUT carries its data there.
  • 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.

MethodPurposeBodySafeIdempotent
GETRetrieve a resourceNoβœ…βœ…
POSTCreate a resource / submit dataYes❌❌
PUTReplace a resource entirelyYesβŒβœ…
PATCHPartially update a resourceYes❌❌
DELETERemove a resourceSometimesβŒβœ…
HEADLike GET, headers onlyNoβœ…βœ…
OPTIONSAsk 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.

RangeMeaningMnemonic
1xxInformational"Hold on…"
2xxSuccess"Here you go"
3xxRedirection"Go look over there"
4xxClient error"You messed up"
5xxServer 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

HeaderWhat it does
Strict-Transport-SecurityForces browsers to use HTTPS for your site
Content-Security-PolicyRestricts which sources of scripts/styles may load β€” a strong XSS defence
X-Content-Type-Options: nosniffStops the browser from guessing (sniffing) content types
Access-Control-Allow-OriginThe 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.

sequenceDiagram participant Browser participant DNS as DNS server participant Server participant DB as Database Browser->>DNS: 1. Look up example.com DNS-->>Browser: IP address Browser->>+Server: 2. TCP + TLS handshake Server-->>-Browser: Secure connection ready Browser->>+Server: 3. GET /products Server->>+DB: 4. Query products DB-->>-Server: Rows Server-->>-Browser: 5. 200 OK + HTML/JSON Note over Browser: 6. Render & fetch sub-resources
  1. DNS resolution β€” translate the domain name into an IP address.
  2. Connection β€” a TCP handshake, and for HTTPS a TLS handshake to encrypt the channel.
  3. Request β€” the browser sends the HTTP request over that connection.
  4. Server processing β€” the server routes the request, runs logic, and queries data stores.
  5. Response β€” the server sends back a status code, headers, and a body.
  6. 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.