Introduction to HTTP
HTTP (Hypertext Transfer Protocol) is the backbone of data communication on the web. It defines the format and rules for how messages are sent and received between clients (usually web browsers) and servers.
Since its creation in the early 1990s by Tim Berners-Lee, HTTP has evolved through several versions (HTTP/1.0, HTTP/1.1, HTTP/2, and HTTP/3), each bringing improvements in performance, security, and functionality.
As backend developers, understanding HTTP is essential because it's the primary way clients will communicate with your server-side code. Almost every web application you build will involve handling HTTP requests and constructing HTTP responses.
The Mail System Analogy
A helpful way to understand HTTP is to compare it to a postal mail system:
-
HTTP Request = Sending a Letter
- The envelope (HTTP headers) contains addressing information and instructions
- The letter inside (request body) contains the actual content or data
- The postal service (internet infrastructure) delivers the letter to the correct address
-
HTTP Response = Reply Letter
- The recipient (server) reads your letter and prepares a response
- The response envelope (HTTP response headers) contains metadata about the reply
- The response letter (response body) contains the information you requested
- The postal worker (internet) delivers the response back to you
-
HTTP Status Codes = Delivery Status Notifications
- Success (200 OK) = "Your letter was delivered and here's the reply"
- Redirection (302 Found) = "The recipient has moved; I've forwarded your letter"
- Client Error (404 Not Found) = "No one at this address; couldn't deliver your letter"
- Server Error (500 Internal Server Error) = "The recipient got your letter but had a problem responding"
Just like postal mail, HTTP follows established formats and protocols to ensure that messages are properly delivered, processed, and responded to.
The HTTP Request in Detail
Anatomy of an HTTP Request
Start Line (Request Line)
The first line of an HTTP request contains three important pieces of information:
- HTTP Method: Defines the action to be performed (GET, POST, PUT, DELETE, etc.)
- Request URL: The resource path on the server (e.g., /users/profile, /api/products)
- HTTP Version: The version of the HTTP protocol being used (e.g., HTTP/1.1, HTTP/2)
Request Headers
Headers provide additional information about the request or the client:
- Host: The domain name of the server (required in HTTP/1.1)
- User-Agent: Information about the client making the request
- Accept: Content types the client can process
- Content-Type: Format of the data in the request body
- Authorization: Credentials for authentication
- Cookie: Previously stored cookies
- Cache-Control: Directives for caching mechanisms
Request Body
Present in some requests (POST, PUT), it contains the data being sent to the server:
- Form data
- JSON objects
- XML content
- File uploads
- Other types of data
HTTP Methods
HTTP methods indicate the desired action to be performed on a resource:
| Method | Description | Has Body | Idempotent | Safe |
|---|---|---|---|---|
| GET | Retrieve a resource | No | Yes | Yes |
| POST | Create a new resource or submit data for processing | Yes | No | No |
| PUT | Update a resource by replacing it entirely | Yes | Yes | No |
| PATCH | Partially update a resource | Yes | No | No |
| DELETE | Remove a resource | Sometimes | Yes | No |
| HEAD | Same as GET but without response body (headers only) | No | Yes | Yes |
| OPTIONS | Describe communication options for a resource | No | Yes | Yes |
Important concepts:
- Safe: Does not alter the state of the server (read-only)
- Idempotent: Multiple identical requests have the same effect as a single request
HTTP Request Examples
Simple GET Request
GET /api/products HTTP/1.1
Host: example.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36
Accept: application/json
POST Request with JSON Data
POST /api/users HTTP/1.1
Host: example.com
Content-Type: application/json
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Length: 84
{
"name": "John Doe",
"email": "john.doe@example.com",
"password": "securePassword123"
}
PUT Request to Update a Resource
PUT /api/products/42 HTTP/1.1
Host: example.com
Content-Type: application/json
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Length: 145
{
"id": 42,
"name": "Updated Product Name",
"price": 99.99,
"description": "This is an updated product description",
"in_stock": true
}
The HTTP Response in Detail
Anatomy of an HTTP Response
Status Line
The first line of an HTTP response contains:
- HTTP Version: Protocol version used by the server
- Status Code: Three-digit numerical code indicating the result of the request
- Reason Phrase: A brief textual description of the status code
Response Headers
Headers provide metadata about the response:
- Content-Type: Format of the response body
- Content-Length: Size of the response body in bytes
- Server: Information about the server software
- Date: Timestamp when the response was generated
- Cache-Control: Directives for caching the response
- Set-Cookie: Cookies to be stored by the client
- Location: URL for redirection (in 3xx responses)
Response Body
Contains the requested resource or data:
- HTML documents
- JSON or XML data
- Binary data (images, files, etc.)
- Error details
- May be empty for some responses (204 No Content, 304 Not Modified)
HTTP Status Codes
Status codes are grouped into five categories:
1xx: Informational
- 100 Continue: Request received, continue with the request
- 101 Switching Protocols: Server is switching protocols as requested
- 102 Processing: Server has received the request but is still processing it
2xx: Success
- 200 OK: Request succeeded
- 201 Created: Request succeeded and a new resource was created
- 204 No Content: Request succeeded but no content to return
- 206 Partial Content: Server is delivering only part of the resource
3xx: Redirection
- 301 Moved Permanently: Resource has been permanently moved to a new URL
- 302 Found: Resource temporarily located at a different URL
- 304 Not Modified: Resource hasn't changed since last request
- 307 Temporary Redirect: Temporary redirect that preserves the HTTP method
4xx: Client Errors
- 400 Bad Request: Server couldn't understand the request
- 401 Unauthorized: Authentication required
- 403 Forbidden: Server understood but refuses to authorize
- 404 Not Found: Resource not found
- 405 Method Not Allowed: HTTP method not supported for this resource
- 429 Too Many Requests: Client has sent too many requests
5xx: Server Errors
- 500 Internal Server Error: Unexpected server error
- 501 Not Implemented: Server doesn't support the functionality
- 502 Bad Gateway: Server acting as gateway received invalid response
- 503 Service Unavailable: Server temporarily unable to handle request
- 504 Gateway Timeout: Gateway didn't receive timely response
HTTP Response Examples
Successful Response with JSON Data
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 165
Date: Mon, 15 Aug 2023 10:23:45 GMT
Cache-Control: max-age=3600
{
"id": 42,
"name": "Wireless Headphones",
"price": 99.99,
"description": "High-quality wireless headphones with noise cancellation",
"in_stock": true,
"categories": ["electronics", "audio"]
}
Redirection Response
HTTP/1.1 301 Moved Permanently
Location: https://example.com/new-location
Content-Length: 0
Date: Mon, 15 Aug 2023 10:24:35 GMT
Client Error Response
HTTP/1.1 404 Not Found
Content-Type: application/json
Content-Length: 49
Date: Mon, 15 Aug 2023 10:25:12 GMT
{
"error": "Product with ID 999 could not be found"
}
Server Error Response
HTTP/1.1 500 Internal Server Error
Content-Type: application/json
Content-Length: 83
Date: Mon, 15 Aug 2023 10:26:05 GMT
{
"error": "An unexpected error occurred while processing your request"
}
The Complete HTTP Request/Response Cycle
Let's break down the complete cycle from when a user enters a URL until the page appears:
Step 1: DNS Resolution
- User enters a URL (e.g.,
https://example.com/products) - Browser extracts the domain name (example.com)
- Browser queries DNS servers to translate the domain name to an IP address
- DNS server responds with the IP address of the server hosting example.com
Step 2: Connection Establishment
- Browser initiates a TCP connection to the server's IP address (typically port 80 for HTTP or 443 for HTTPS)
- TCP three-way handshake occurs to establish the connection
- If using HTTPS, a TLS handshake follows to establish an encrypted connection
Step 3: HTTP Request
- Browser constructs an HTTP request:
- Method: GET
- Path: /products
- Headers: Host, User-Agent, Accept, etc.
- Request is sent to the server over the established connection
Step 4: Server Processing
- Server receives and parses the HTTP request
- Server determines how to handle the request based on the path, method, and headers
- Server may interact with databases, file systems, or other services to gather data
- Server processes the request according to application logic
Step 5: HTTP Response
- Server constructs an HTTP response:
- Status code: (e.g., 200 OK if successful)
- Headers: Content-Type, Content-Length, etc.
- Body: The requested resource (HTML, JSON, etc.)
- Response is sent back to the client over the same connection
Step 6: Client Processing
- Browser receives and parses the HTTP response
- If the response contains HTML, the browser begins rendering it
- Browser may discover references to additional resources (CSS, JavaScript, images) and initiate new requests for them
- JavaScript is executed, potentially triggering additional AJAX requests
Step 7: Connection Handling
- Connection may be kept alive for subsequent requests (HTTP keep-alive) or closed
- With HTTP/2 or HTTP/3, multiple requests/responses can be multiplexed over a single connection
HTTP in Different Context Types
Traditional Web Pages
Content-Type: text/html Browser->>+Server: GET /styles.css HTTP/1.1 Server->>-Browser: HTTP/1.1 200 OK
Content-Type: text/css Browser->>+Server: GET /script.js HTTP/1.1 Server->>-Browser: HTTP/1.1 200 OK
Content-Type: application/javascript Browser->>+Server: GET /image.jpg HTTP/1.1 Server->>-Browser: HTTP/1.1 200 OK
Content-Type: image/jpeg
In traditional web pages:
- Initial request fetches HTML document
- Browser parses HTML and discovers linked resources
- Multiple subsequent requests fetch CSS, JavaScript, images, etc.
- Page navigation typically requires a new full HTTP request cycle
AJAX and Single-Page Applications
Content-Type: text/html Note over Browser: User interacts with app Browser->>+Server: GET /api/data HTTP/1.1
Accept: application/json Server->>-Browser: HTTP/1.1 200 OK
Content-Type: application/json Note over Browser: App updates UI without
page reload Browser->>+Server: POST /api/items HTTP/1.1
Content-Type: application/json Server->>-Browser: HTTP/1.1 201 Created
Content-Type: application/json
In AJAX and SPA contexts:
- Initial page load fetches application shell
- Subsequent interactions use AJAX (Asynchronous JavaScript and XML) to fetch data
- Requests often use JSON format instead of HTML
- Responses update specific parts of the page without full reloads
- HTTP status codes and headers are still important but handled by JavaScript
RESTful APIs
[User list data] Client->>+API: GET /api/users/42 HTTP/1.1 API->>-Client: HTTP/1.1 200 OK
[Single user data] Client->>+API: POST /api/users HTTP/1.1
[New user data] API->>-Client: HTTP/1.1 201 Created
[Created user data] Client->>+API: PUT /api/users/42 HTTP/1.1
[Updated user data] API->>-Client: HTTP/1.1 200 OK
[Updated user data] Client->>+API: DELETE /api/users/42 HTTP/1.1 API->>-Client: HTTP/1.1 204 No Content
In RESTful API contexts:
- HTTP methods align with CRUD operations (Create, Read, Update, Delete)
- Resource-oriented design with hierarchical URLs
- Emphasis on appropriate status codes
- Content negotiation through Accept and Content-Type headers
- Statelessness: each request contains all information needed
Mobile Applications
In mobile app contexts:
- HTTP requests often communicate with backend APIs
- Authentication typically uses tokens in Authorization header
- Efficient data formats prioritized (JSON, Protocol Buffers)
- Bandwidth and battery considerations influence request patterns
- Handling offline scenarios and synchronization
HTTP Headers in Depth
HTTP headers play a critical role in controlling the behavior of requests and responses. Let's examine some important headers in detail:
Request Headers
General Headers (used in both requests and responses)
-
Cache-Control: Directives for caching mechanisms
Cache-Control: no-cache- Validate with server before using cached versionCache-Control: max-age=3600- Cache is valid for 3600 seconds
-
Connection: Control options for the connection
Connection: keep-alive- Keep connection open for multiple requestsConnection: close- Close connection after response
- Date: Date and time the message was sent
Request-Specific Headers
-
Accept: Media types the client can process
Accept: text/html- Client prefers HTMLAccept: application/json, text/plain;q=0.5- Prefers JSON, accepts plain text as fallback
-
Accept-Language: Preferred languages
Accept-Language: en-US, en;q=0.8, fr;q=0.6- Prefers US English, then English, then French
-
Authorization: Authentication credentials
Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=- Basic authenticationAuthorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...- JWT token
-
Content-Type: Format of the request body
Content-Type: application/json- JSON dataContent-Type: application/x-www-form-urlencoded- Form dataContent-Type: multipart/form-data- Form with file uploads
-
Cookie: Cookies previously set by the server
Cookie: sessionId=abc123; userId=42
-
User-Agent: Client software identification
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ...
-
Referer: URL of the page that contained the link to the requested resource
Referer: https://example.com/products
Response Headers
Response-Specific Headers
-
Content-Type: Format of the response body
Content-Type: text/html; charset=UTF-8- HTML content with UTF-8 encodingContent-Type: application/json- JSON data
-
Content-Length: Size of the response body in bytes
Content-Length: 2048- Response is 2048 bytes
-
Set-Cookie: Cookies to be stored by the client
Set-Cookie: sessionId=abc123; Path=/; HttpOnly; Secure
-
Location: URL for redirection
Location: https://example.com/new-location
-
Access-Control-Allow-Origin: CORS header specifying allowed origins
Access-Control-Allow-Origin: *- Allow any originAccess-Control-Allow-Origin: https://example.com- Allow specific origin
-
ETag: Version identifier for the resource
ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"
-
WWW-Authenticate: Authentication method when 401 Unauthorized is returned
WWW-Authenticate: Basic realm="Access to the staging site"
Security-Related Headers
-
Content-Security-Policy: Controls resources the client is allowed to load
Content-Security-Policy: default-src 'self'; script-src 'self' trusted.com
-
Strict-Transport-Security: Forces HTTPS connections
Strict-Transport-Security: max-age=31536000; includeSubDomains
-
X-XSS-Protection: Controls browser's XSS filtering
X-XSS-Protection: 1; mode=block
-
X-Content-Type-Options: Prevents MIME type sniffing
X-Content-Type-Options: nosniff
Evolution of HTTP
HTTP/1.1
The stable workhorse of the web for over two decades:
- Text-based protocol, human-readable
- Introduced persistent connections (keep-alive)
- Added host header to support virtual hosting
- Request pipelining (multiple requests before responses)
- Content negotiation mechanisms
- Challenges: Head-of-line blocking, inefficient header compression
HTTP/2
Major performance improvements:
- Binary protocol instead of text-based
- Multiplexing multiple requests/responses over a single connection
- Header compression with HPACK
- Server push (server can send resources before client requests them)
- Stream prioritization
- Backwards compatible with HTTP/1.1 semantics
HTTP/3
The latest evolution, built on QUIC:
- Uses QUIC transport protocol instead of TCP
- Built-in TLS encryption
- Improved connection migration
- Reduced connection setup latency
- Eliminates head-of-line blocking at the transport level
- Better performance on unreliable networks
Working with HTTP in Different Languages
As backend developers, you'll need to handle HTTP requests and responses in your code. Let's look at some examples in different languages:
Node.js with Express
// Simple Express server handling different HTTP methods
const express = require('express');
const app = express();
app.use(express.json());
// GET request handler
app.get('/api/users', (req, res) => {
// Access query parameters
const page = req.query.page || 1;
const limit = req.query.limit || 10;
// Access headers
const acceptLanguage = req.headers['accept-language'];
// Send response
res.status(200).json({
users: [{ id: 1, name: 'John' }, { id: 2, name: 'Jane' }],
page: page,
limit: limit,
total: 100
});
});
// POST request handler
app.post('/api/users', (req, res) => {
// Access request body
const { name, email } = req.body;
// Validate input
if (!name || !email) {
return res.status(400).json({ error: 'Name and email are required' });
}
// Process the data (in a real app, you'd save to a database)
const newUser = { id: 3, name, email };
// Send response with status code 201 Created
res.status(201).json(newUser);
});
// PUT request handler
app.put('/api/users/:id', (req, res) => {
// Access route parameters
const userId = req.params.id;
const userData = req.body;
// In a real app, you'd update the user in the database
res.status(200).json({
id: userId,
...userData,
updated: true
});
});
// DELETE request handler
app.delete('/api/users/:id', (req, res) => {
// Access route parameters
const userId = req.params.id;
// In a real app, you'd delete the user from the database
// Send 204 No Content response
res.status(204).send();
});
app.listen(3000, () => {
console.log('Server running on port 3000');
});
Python with Flask
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route('/api/users', methods=['GET'])
def get_users():
# Access query parameters
page = request.args.get('page', 1, type=int)
limit = request.args.get('limit', 10, type=int)
# Access headers
accept_language = request.headers.get('Accept-Language')
# Send response
return jsonify({
'users': [{'id': 1, 'name': 'John'}, {'id': 2, 'name': 'Jane'}],
'page': page,
'limit': limit,
'total': 100
}), 200 # Status code
@app.route('/api/users', methods=['POST'])
def create_user():
# Access JSON body
data = request.get_json()
name = data.get('name')
email = data.get('email')
# Validate input
if not name or not email:
return jsonify({'error': 'Name and email are required'}), 400
# Process the data (in a real app, you'd save to a database)
new_user = {'id': 3, 'name': name, 'email': email}
# Return with 201 Created status
return jsonify(new_user), 201
@app.route('/api/users/', methods=['PUT'])
def update_user(user_id):
# Access route parameter
data = request.get_json()
# In a real app, you'd update the user in the database
# Return updated user
user_data = {**data, 'id': user_id, 'updated': True}
return jsonify(user_data), 200
@app.route('/api/users/', methods=['DELETE'])
def delete_user(user_id):
# In a real app, you'd delete the user from the database
# Return 204 No Content
return '', 204
if __name__ == '__main__':
app.run(debug=True)
PHP (Laravel-inspired)
<?php
// Simulate route handling for different HTTP methods
$method = $_SERVER['REQUEST_METHOD'];
$path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
// GET request handler
if ($method === 'GET' && $path === '/api/users') {
// Access query parameters
$page = $_GET['page'] ?? 1;
$limit = $_GET['limit'] ?? 10;
// Access headers
$acceptLanguage = $_SERVER['HTTP_ACCEPT_LANGUAGE'] ?? '';
// Set response headers
header('Content-Type: application/json');
http_response_code(200);
// Send response
echo json_encode([
'users' => [
['id' => 1, 'name' => 'John'],
['id' => 2, 'name' => 'Jane']
],
'page' => (int)$page,
'limit' => (int)$limit,
'total' => 100
]);
}
// POST request handler
else if ($method === 'POST' && $path === '/api/users') {
// Get JSON body
$data = json_decode(file_get_contents('php://input'), true);
$name = $data['name'] ?? null;
$email = $data['email'] ?? null;
// Validate input
if (!$name || !$email) {
header('Content-Type: application/json');
http_response_code(400);
echo json_encode(['error' => 'Name and email are required']);
exit;
}
// Process the data (in a real app, you'd save to a database)
$newUser = ['id' => 3, 'name' => $name, 'email' => $email];
// Send response
header('Content-Type: application/json');
http_response_code(201);
echo json_encode($newUser);
}
// PUT request handler - assumes a route like /api/users/42
else if ($method === 'PUT' && preg_match('#^/api/users/(\d+)$#', $path, $matches)) {
$userId = $matches[1];
// Get JSON body
$data = json_decode(file_get_contents('php://input'), true);
// In a real app, you'd update the user in the database
// Send response
header('Content-Type: application/json');
http_response_code(200);
echo json_encode(array_merge($data, [
'id' => (int)$userId,
'updated' => true
]));
}
// DELETE request handler
else if ($method === 'DELETE' && preg_match('#^/api/users/(\d+)$#', $path, $matches)) {
$userId = $matches[1];
// In a real app, you'd delete the user from the database
// Send 204 No Content response
http_response_code(204);
}
else {
// Route not found
header('Content-Type: application/json');
http_response_code(404);
echo json_encode(['error' => 'Route not found']);
}
HTTP Best Practices for Backend Developers
RESTful API Design
- Use appropriate HTTP methods for each operation (GET, POST, PUT, DELETE)
- Structure URLs around resources, not actions
- Use nouns in plural form for resource collections (/users, /products)
- Use hierarchical relationships (/users/42/orders)
- Return appropriate status codes for different scenarios
- Implement proper error responses with helpful messages
- Use consistent data formats for requests and responses
Security Considerations
- Always use HTTPS in production
- Implement proper authentication and authorization
- Validate all input data to prevent injection attacks
- Use security headers (Content-Security-Policy, Strict-Transport-Security)
- Implement rate limiting to prevent abuse
- Set appropriate CORS headers to control cross-origin requests
- Use secure cookie settings (HttpOnly, Secure, SameSite)
- Don't expose sensitive information in URLs or error messages
Performance Optimization
- Implement proper caching with Cache-Control headers
- Use conditional requests with ETag or Last-Modified
- Compress responses with gzip or brotli
- Minimize payload sizes (pagination, field filtering)
- Consider implementing HTTP/2 or HTTP/3 for performance benefits
- Use connection pooling for database and external service connections
- Monitor and optimize response times
Versioning and Evolution
- Include API version in URL (/v1/users) or Accept header
- Maintain backward compatibility when possible
- Document API changes clearly
- Use deprecation headers to signal upcoming changes
- Consider using API gateways for managing multiple versions
Practical Exercise: Analyzing HTTP Communication
Task: Decode and Analyze HTTP Messages
For this exercise, you'll practice interpreting HTTP requests and responses and understanding what's happening in each part.
Example 1: Analyze this HTTP Request
POST /api/login HTTP/1.1
Host: example.com
Content-Type: application/json
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)
Content-Length: 56
{
"username": "john.doe",
"password": "secretpassword123"
}
Questions:
- What operation is being performed?
- What data format is being sent?
- What is the likely purpose of this request?
- Are there any security concerns with this request?
Example 2: Match Requests with Appropriate Responses
Given this request:
GET /api/products?category=electronics&sort=price HTTP/1.1
Host: example.com
Accept: application/json
Which of these responses is most appropriate?
Response A:
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 256
{
"products": [
{ "id": 101, "name": "Wireless Earbuds", "price": 29.99, "category": "electronics" },
{ "id": 102, "name": "USB Cable", "price": 9.99, "category": "electronics" },
{ "id": 103, "name": "Smartphone", "price": 499.99, "category": "electronics" }
],
"total": 3
}
Response B:
HTTP/1.1 201 Created
Content-Type: application/json
Content-Length: 85
{
"id": 104,
"name": "New Product",
"price": 199.99,
"category": "electronics"
}
Response C:
HTTP/1.1 405 Method Not Allowed
Content-Type: application/json
Content-Length: 49
Allow: POST
{
"error": "GET method not allowed for this endpoint"
}
Example 3: Create a Complete HTTP Cycle
Design a complete HTTP request and response cycle for a user updating their profile information. Include:
- The appropriate HTTP request with all necessary components
- A successful response
- A response for when validation fails
- A response for when the user is not authenticated
Summary
In this session, we've explored the HTTP request/response cycle in detail:
- The structure and components of HTTP requests and responses
- HTTP methods and their appropriate usage
- Status codes and their categories
- Important headers and their functions
- The complete cycle from DNS lookup to page rendering
- HTTP evolution from HTTP/1.1 to HTTP/3
- How to work with HTTP in different backend languages
- Best practices for RESTful API design
Understanding HTTP is fundamental to backend development as it forms the primary interface between clients and your server-side code. In upcoming sessions, we'll build on this foundation to implement specific backend functionality using Node.js, Python, and PHP.
Additional Resources
- MDN Web Docs: HTTP - Comprehensive HTTP documentation
- HTTP Working Group Specifications - Official HTTP specifications
- HTTP Status Codes - Reference for all HTTP status codes
- RESTful API Design - Guidelines for API design
- JWT.io - Introduction to JSON Web Tokens
- HTTP Toolkit - Tool for viewing, mocking, and debugging HTTP