Introduction to HTTP Protocol Components
Yesterday, we introduced the HTTP request/response cycle as the foundation for web communication. Today, we'll dive deeper into the specific components that make up HTTP communications: methods (verbs), headers, and status codes.
Understanding these components in depth is crucial for backend developers because they determine how our applications communicate with clients, handle different types of requests, transmit metadata, and indicate the results of operations. Mastering these elements will help you build more robust, secure, and efficient web applications.
The Language Analogy
Think of HTTP as a language for communication between clients and servers, similar to how humans communicate:
- HTTP Methods are like Verbs: They indicate the action being requested (GET = "Please give me", POST = "Please take this", DELETE = "Please remove this").
- URLs are like Nouns: They identify what resource the action applies to (/users/123 = "the user with ID 123").
- Headers are like Adjectives and Adverbs: They provide additional details about the communication (Accept: application/json = "Please respond in JSON format").
- Status Codes are like Short Responses: They quickly indicate the outcome (200 OK = "Yes, I did what you asked", 404 Not Found = "I couldn't find what you asked for").
- Body Content is like the Main Message: It contains the detailed information being exchanged.
Just as fluency in a language requires understanding both vocabulary and grammar, mastery of HTTP requires understanding both the individual components and how they work together in standard patterns.
HTTP Methods (Verbs) In Depth
HTTP methods, sometimes called verbs, define the operation that the client wants to perform on a resource. Let's explore each major method in detail:
GET: Requesting Resources
- Purpose: Retrieve data from the server
- Characteristics:
- Data is sent in the URL (query parameters)
- Requests should never cause side effects (no data modification)
- Responses are cacheable
- Bookmarkable and can be shared (all data is in URL)
- Examples:
- Load a webpage
- Fetch an API resource
- Download an image or file
- Typical Response Codes: 200 OK, 304 Not Modified, 404 Not Found
- Code Example:
// Express.js (Node.js) GET handler
app.get('/api/products/:id', (req, res) => {
const productId = req.params.id;
// Retrieve product from database
const product = database.getProduct(productId);
if (!product) {
return res.status(404).json({ error: 'Product not found' });
}
res.status(200).json(product);
});
POST: Creating Resources
(with product data) Note right of Server: Creates new product Server->>-Client: 201 Created with new product ID
- Purpose: Submit data to create a new resource
- Characteristics:
- Data is sent in the request body
- Not idempotent (multiple identical requests create multiple resources)
- Typically not cacheable
- Can send large amounts of data
- Examples:
- Submit a form
- Create a new user
- Add a new product
- Upload a file
- Typical Response Codes: 201 Created, 400 Bad Request, 409 Conflict
- Code Example:
// Express.js (Node.js) POST handler
app.post('/api/products', (req, res) => {
const productData = req.body;
// Validate input
if (!productData.name || !productData.price) {
return res.status(400).json({ error: 'Name and price are required' });
}
// Create product in database
const newProduct = database.createProduct(productData);
// Return created product with 201 status
res.status(201).json({
id: newProduct.id,
message: 'Product created successfully',
product: newProduct
});
});
PUT: Updating Resources (Complete Replacement)
(with complete product data) Note right of Server: Replaces existing product Server->>-Client: 200 OK with updated product
- Purpose: Update an existing resource by replacing it entirely
- Characteristics:
- Data is sent in the request body
- Idempotent (multiple identical requests have the same effect)
- Requires the complete resource representation
- Creates the resource if it doesn't exist (sometimes)
- Examples:
- Update a product with new information
- Replace a configuration file
- Update user profile
- Typical Response Codes: 200 OK, 204 No Content, 400 Bad Request, 404 Not Found
- Code Example:
// Express.js (Node.js) PUT handler
app.put('/api/products/:id', (req, res) => {
const productId = req.params.id;
const productData = req.body;
// Validate input
if (!productData.name || !productData.price || !productData.description) {
return res.status(400).json({ error: 'Complete product data required' });
}
// Check if product exists
const existingProduct = database.getProduct(productId);
if (!existingProduct) {
return res.status(404).json({ error: 'Product not found' });
}
// Update product (complete replacement)
const updatedProduct = database.replaceProduct(productId, productData);
res.status(200).json(updatedProduct);
});
PATCH: Partial Updates
(with partial product data) Note right of Server: Updates specific fields Server->>-Client: 200 OK with updated product
- Purpose: Partially update an existing resource
- Characteristics:
- Only sends the fields that need to be updated
- Not necessarily idempotent
- More efficient than PUT for small changes
- Resource must exist (doesn't create new resources)
- Examples:
- Update a user's email address
- Change a product's price
- Update specific settings
- Typical Response Codes: 200 OK, 204 No Content, 404 Not Found
- Code Example:
// Express.js (Node.js) PATCH handler
app.patch('/api/products/:id', (req, res) => {
const productId = req.params.id;
const updates = req.body;
// Check if product exists
const existingProduct = database.getProduct(productId);
if (!existingProduct) {
return res.status(404).json({ error: 'Product not found' });
}
// Apply partial updates
const updatedProduct = database.updateProduct(productId, updates);
res.status(200).json(updatedProduct);
});
DELETE: Removing Resources
- Purpose: Remove an existing resource
- Characteristics:
- Usually doesn't include a request body
- Idempotent (deleting twice has same effect as deleting once)
- Response typically has no body
- Examples:
- Delete a user account
- Remove a product
- Clear a resource
- Typical Response Codes: 204 No Content, 404 Not Found, 403 Forbidden
- Code Example:
// Express.js (Node.js) DELETE handler
app.delete('/api/products/:id', (req, res) => {
const productId = req.params.id;
// Check if product exists
const existingProduct = database.getProduct(productId);
if (!existingProduct) {
return res.status(404).json({ error: 'Product not found' });
}
// Remove product
database.deleteProduct(productId);
// Return 204 No Content
res.status(204).send();
});
Additional Methods
-
HEAD: Like GET but returns only headers, no body
- Useful for checking if a resource exists or has been modified
- More efficient when you don't need the resource content
-
OPTIONS: Returns the HTTP methods supported by the server for the specified URL
- Used in CORS preflight requests
- Helps clients understand what operations are allowed
-
TRACE: Echoes back the received request
- Used for debugging
- Often disabled for security reasons
-
CONNECT: Establishes a tunnel to the server
- Used for SSL tunneling
- Common with proxy servers
HTTP Headers In Depth
HTTP headers provide additional information about the request or response. They're like metadata that helps clients and servers understand how to process the messages.
Header Categories
General Headers
Apply to both requests and responses:
-
Cache-Control: Controls caching behavior
Cache-Control: no-cache- Must revalidate with server before using cached copyCache-Control: max-age=3600- Cache is valid for 3600 secondsCache-Control: no-store- Don't cache at allCache-Control: private- Only cacheable by browser, not intermediaries
-
Connection: Controls whether the connection stays open after the current transaction finishes
Connection: keep-alive- Keep connection open for future requestsConnection: close- Close connection after this request/response
-
Transfer-Encoding: Specifies encoding transformations applied to the message body
Transfer-Encoding: chunked- Body sent in a series of chunksTransfer-Encoding: gzip- Body is compressed with gzip
-
Date: Date and time when the message was originated
Date: Wed, 21 Oct 2023 07:28:00 GMT
Request Headers
Sent by the client in HTTP requests:
-
Accept: Media types the client can process
Accept: text/html- Client prefers HTML responsesAccept: application/json- Client prefers JSON responsesAccept: image/*- Client accepts any image format
-
Authorization: Credentials for authenticating the client
Authorization: Basic ZGVtbzpwQDU1dzByZA==- Basic auth (Base64 encoded username:password)Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...- OAuth or JWT token
-
User-Agent: Information about the client software
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36...
-
Referer: URL of the page that linked to the requested resource
Referer: https://www.example.com/page1.html
-
Host: Domain name of the server (required in HTTP/1.1)
Host: api.example.com
-
Cookie: HTTP cookies previously sent by the server
Cookie: sessionId=abc123; userId=42
-
Accept-Language: Preferred languages for the response
Accept-Language: en-US, en;q=0.8, fr;q=0.5
-
If-Modified-Since: Makes request conditional based on modification date
If-Modified-Since: Wed, 21 Oct 2023 07:28:00 GMT
Response Headers
Sent by the server in HTTP responses:
-
Server: Information about the server software
Server: Apache/2.4.41 (Unix)
-
Set-Cookie: Set HTTP cookies on the client
Set-Cookie: sessionId=abc123; Path=/; HttpOnly; Secure
-
WWW-Authenticate: Authentication method that should be used
WWW-Authenticate: Basic realm="User Visible Realm"
-
Access-Control-Allow-Origin: CORS header that specifies allowed origins
Access-Control-Allow-Origin: *- Allow any originAccess-Control-Allow-Origin: https://example.com- Specific origin
-
Location: Used for redirects
Location: https://www.example.com/new-page
Entity Headers
Describe the content being transferred:
-
Content-Type: MIME type of the body content
Content-Type: text/html; charset=UTF-8Content-Type: application/jsonContent-Type: image/jpeg
-
Content-Length: Size of the body in bytes
Content-Length: 348
-
Content-Encoding: Encoding applied to the body
Content-Encoding: gzip
-
Content-Language: Natural language of the content
Content-Language: en-US
-
Expires: Date/time after which the response is considered stale
Expires: Wed, 21 Oct 2023 07:28:00 GMT
-
Last-Modified: Date/time when the resource was last modified
Last-Modified: Wed, 21 Oct 2023 07:28:00 GMT
Security-Related Headers
Modern web applications should include security headers to protect against common attacks:
-
Content-Security-Policy: Controls which resources the client can 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-Content-Type-Options: Prevents MIME type sniffing
X-Content-Type-Options: nosniff
-
X-Frame-Options: Controls whether the page can be displayed in frames
X-Frame-Options: DENYX-Frame-Options: SAMEORIGIN
-
X-XSS-Protection: Controls browser's XSS filter
X-XSS-Protection: 1; mode=block
Working with Headers in Different Backend Languages
Node.js (Express)
// Reading request headers
app.get('/api/products', (req, res) => {
const acceptHeader = req.header('Accept');
const userAgent = req.header('User-Agent');
// Setting response headers
res.set('Content-Type', 'application/json');
res.set('Cache-Control', 'max-age=3600');
// Security headers
res.set('Content-Security-Policy', "default-src 'self'");
res.set('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
// Send response
res.status(200).json({ products: [...] });
});
Python (Flask)
from flask import request, jsonify
@app.route('/api/products')
def get_products():
# Reading request headers
accept_header = request.headers.get('Accept')
user_agent = request.headers.get('User-Agent')
response = jsonify({'products': [...]})
# Setting response headers
response.headers['Content-Type'] = 'application/json'
response.headers['Cache-Control'] = 'max-age=3600'
# Security headers
response.headers['Content-Security-Policy'] = "default-src 'self'"
response.headers['Strict-Transport-Security'] = 'max-age=31536000; includeSubDomains'
return response
PHP
<?php
// Reading request headers
$acceptHeader = $_SERVER['HTTP_ACCEPT'] ?? '';
$userAgent = $_SERVER['HTTP_USER_AGENT'] ?? '';
// Setting response headers
header('Content-Type: application/json');
header('Cache-Control: max-age=3600');
// Security headers
header("Content-Security-Policy: default-src 'self'");
header('Strict-Transport-Security: max-age=31536000; includeSubDomains');
// Send response
echo json_encode(['products' => [...]]);
?>
HTTP Status Codes In Depth
HTTP status codes are three-digit numbers that indicate the outcome of an HTTP request. They're grouped into five classes, each with a general meaning:
1xx: Informational
These status codes indicate that the request was received and the process is continuing.
-
100 Continue: The server has received the request headers and the client should proceed to send the request body.
- Used for large uploads to avoid sending unnecessary data
- Client sends
Expect: 100-continueheader first
-
101 Switching Protocols: The server is switching protocols as requested by the client.
- Used in WebSocket connections
- Client sends
Upgrade: websocketheader
-
102 Processing: The server has received and is processing the request, but no response is available yet.
- Prevents client timeouts for long-running operations
2xx: Success
These status codes indicate that the request was successfully received, understood, and accepted.
-
200 OK: The request has succeeded.
- Standard success response
- Used for GET, POST, PUT, etc.
- Response typically includes a body
-
201 Created: The request has been fulfilled and a new resource has been created.
- Typically used for POST requests
- Response should include a Location header pointing to the new resource
- Response body usually includes the created resource
-
204 No Content: The request has succeeded but there is no content to return.
- Often used for DELETE operations
- Also used for PUT or PATCH when updates don't need to return data
- Response has no body
-
206 Partial Content: The server is delivering only part of the resource.
- Used for range requests (e.g., resumable downloads)
- Response includes
Content-Rangeheader
3xx: Redirection
These status codes indicate that further action needs to be taken to complete the request.
-
301 Moved Permanently: The requested resource has been permanently moved to a new URL.
- Client should update bookmarks/references
- Response includes Location header with new URL
- Search engines update their links
-
302 Found: The requested resource is temporarily available at a different URL.
- Client should continue to use the original URL for future requests
- Response includes Location header with temporary URL
- Also known as a temporary redirect
-
304 Not Modified: The resource hasn't changed since the client's last request.
- Used with conditional requests (If-Modified-Since, If-None-Match)
- Response has no body
- Client can use its cached copy
-
307 Temporary Redirect: Like 302, but client must use the same HTTP method for the redirected request.
- Prevents method change from POST to GET
-
308 Permanent Redirect: Like 301, but client must use the same HTTP method for the redirected request.
- Prevents method change from POST to GET
4xx: Client Error
These status codes indicate that the client seems to have made an error.
-
400 Bad Request: The server cannot process the request due to a client error.
- Invalid syntax
- Missing parameters
- Invalid data format
-
401 Unauthorized: Authentication is required and has failed or not been provided.
- Missing or invalid credentials
- Response should include WWW-Authenticate header
- Better name would be "Unauthenticated"
-
403 Forbidden: The client does not have access rights to the content.
- Server understands request but refuses to authorize it
- Authentication won't help
- Based on permissions, not identity
-
404 Not Found: The server cannot find the requested resource.
- Resource doesn't exist
- URL is invalid
- Most common error code
-
405 Method Not Allowed: The request method is not supported for the requested resource.
- e.g., Using POST where only GET is allowed
- Response should include Allow header listing valid methods
-
409 Conflict: The request conflicts with the current state of the resource.
- e.g., Trying to create a resource that already exists
- Version conflicts
-
422 Unprocessable Entity: The request was well-formed but contains semantic errors.
- Often used for validation errors
- Syntactically correct but semantically wrong
-
429 Too Many Requests: The client has sent too many requests in a given time.
- Rate limiting
- Response often includes Retry-After header
5xx: Server Error
These status codes indicate that the server failed to fulfill a valid request.
-
500 Internal Server Error: The server encountered an unexpected condition.
- Generic error message
- Usually indicates programming errors or server issues
-
501 Not Implemented: The server does not support the functionality required to fulfill the request.
- e.g., The server doesn't support a particular HTTP method
-
502 Bad Gateway: The server, while acting as a gateway or proxy, received an invalid response from an upstream server.
- Common in microservices architectures
- Indicates issues with dependent services
-
503 Service Unavailable: The server is not ready to handle the request.
- Temporary overload or maintenance
- Should include Retry-After header when possible
-
504 Gateway Timeout: The server, while acting as a gateway or proxy, did not receive a timely response from an upstream server.
- Dependent service took too long to respond
- Network issues between servers
Working with Status Codes in Different Backend Languages
Node.js (Express)
app.get('/api/products/:id', (req, res) => {
const productId = req.params.id;
// Check if product exists
const product = database.getProduct(productId);
if (!product) {
// 404: Resource not found
return res.status(404).json({
error: 'Product not found',
code: 'PRODUCT_NOT_FOUND'
});
}
// 200: Success
res.status(200).json(product);
});
app.post('/api/products', (req, res) => {
try {
const productData = req.body;
// Validate input
if (!productData.name || !productData.price) {
// 400: Bad Request
return res.status(400).json({
error: 'Name and price are required',
code: 'INVALID_INPUT'
});
}
// Check authorization
if (!req.isAdmin) {
// 403: Forbidden
return res.status(403).json({
error: 'Only administrators can create products',
code: 'PERMISSION_DENIED'
});
}
// Check for conflicts
const existingProduct = database.findProductByName(productData.name);
if (existingProduct) {
// 409: Conflict
return res.status(409).json({
error: 'A product with this name already exists',
code: 'PRODUCT_EXISTS'
});
}
// Create product
const newProduct = database.createProduct(productData);
// 201: Created
res.status(201)
.location(`/api/products/${newProduct.id}`)
.json(newProduct);
} catch (error) {
console.error('Error creating product:', error);
// 500: Internal Server Error
res.status(500).json({
error: 'Failed to create product',
code: 'INTERNAL_ERROR'
});
}
});
Python (Flask)
from flask import request, jsonify
@app.route('/api/products/', methods=['GET'])
def get_product(product_id):
# Check if product exists
product = database.get_product(product_id)
if not product:
# 404: Resource not found
return jsonify({
'error': 'Product not found',
'code': 'PRODUCT_NOT_FOUND'
}), 404
# 200: Success
return jsonify(product), 200
@app.route('/api/products', methods=['POST'])
def create_product():
try:
product_data = request.get_json()
# Validate input
if not product_data.get('name') or not product_data.get('price'):
# 400: Bad Request
return jsonify({
'error': 'Name and price are required',
'code': 'INVALID_INPUT'
}), 400
# Check authorization
if not is_admin():
# 403: Forbidden
return jsonify({
'error': 'Only administrators can create products',
'code': 'PERMISSION_DENIED'
}), 403
# Check for conflicts
existing_product = database.find_product_by_name(product_data['name'])
if existing_product:
# 409: Conflict
return jsonify({
'error': 'A product with this name already exists',
'code': 'PRODUCT_EXISTS'
}), 409
# Create product
new_product = database.create_product(product_data)
# 201: Created
response = jsonify(new_product)
response.status_code = 201
response.headers['Location'] = f"/api/products/{new_product['id']}"
return response
except Exception as e:
app.logger.error(f"Error creating product: {str(e)}")
# 500: Internal Server Error
return jsonify({
'error': 'Failed to create product',
'code': 'INTERNAL_ERROR'
}), 500
PHP
<?php
// Simulate a GET request handler
if ($_SERVER['REQUEST_METHOD'] === 'GET' && preg_match('/\/api\/products\/(\d+)/', $_SERVER['REQUEST_URI'], $matches)) {
$productId = $matches[1];
// Check if product exists
$product = $database->getProduct($productId);
if (!$product) {
// 404: Resource not found
http_response_code(404);
echo json_encode([
'error' => 'Product not found',
'code' => 'PRODUCT_NOT_FOUND'
]);
exit;
}
// 200: Success
http_response_code(200);
echo json_encode($product);
exit;
}
// Simulate a POST request handler
if ($_SERVER['REQUEST_METHOD'] === 'POST' && $_SERVER['REQUEST_URI'] === '/api/products') {
try {
// Get JSON data
$productData = json_decode(file_get_contents('php://input'), true);
// Validate input
if (empty($productData['name']) || empty($productData['price'])) {
// 400: Bad Request
http_response_code(400);
echo json_encode([
'error' => 'Name and price are required',
'code' => 'INVALID_INPUT'
]);
exit;
}
// Check authorization
if (!isAdmin()) {
// 403: Forbidden
http_response_code(403);
echo json_encode([
'error' => 'Only administrators can create products',
'code' => 'PERMISSION_DENIED'
]);
exit;
}
// Check for conflicts
$existingProduct = $database->findProductByName($productData['name']);
if ($existingProduct) {
// 409: Conflict
http_response_code(409);
echo json_encode([
'error' => 'A product with this name already exists',
'code' => 'PRODUCT_EXISTS'
]);
exit;
}
// Create product
$newProduct = $database->createProduct($productData);
// 201: Created
http_response_code(201);
header("Location: /api/products/{$newProduct['id']}");
echo json_encode($newProduct);
exit;
} catch (Exception $e) {
error_log("Error creating product: " . $e->getMessage());
// 500: Internal Server Error
http_response_code(500);
echo json_encode([
'error' => 'Failed to create product',
'code' => 'INTERNAL_ERROR'
]);
exit;
}
}
?>
Best Practices for HTTP Components
HTTP Methods Best Practices
- Use appropriate methods for each operation:
- GET for retrieving data
- POST for creating new resources
- PUT for complete updates
- PATCH for partial updates
- DELETE for removing resources
- Keep GET requests safe and idempotent:
- No side effects
- No data modification
- Make PUT and DELETE idempotent:
- Multiple identical requests should have the same effect as a single request
- Use OPTIONS for CORS preflight:
- Respond with appropriate headers to allow cross-origin requests
HTTP Headers Best Practices
- Set appropriate content headers:
- Content-Type must match actual content
- Content-Length for fixed-size responses
- Implement caching controls:
- Cache-Control for controlling caching behavior
- ETag or Last-Modified for conditional requests
- Include security headers:
- Content-Security-Policy
- Strict-Transport-Security
- X-Content-Type-Options: nosniff
- Set CORS headers appropriately:
- Don't use Access-Control-Allow-Origin: * for authenticated resources
- Only expose necessary headers
- Use secure cookie settings:
- HttpOnly to prevent JavaScript access
- Secure to only send over HTTPS
- SameSite to prevent CSRF attacks
HTTP Status Codes Best Practices
- Use specific status codes:
- Prefer 404 over 200 with error message
- Use 201 for resource creation, not just 200
- Use 204 for successful operations with no content
- Provide error details in the response body:
- Include error code, message, and possibly location
- But don't expose sensitive information
- Use redirects appropriately:
- 301 for permanent moves
- 302/303 for temporary redirects
- 307/308 to preserve the HTTP method
- Include WWW-Authenticate with 401:
- Tell clients how they should authenticate
- Return Retry-After with 429 and 503:
- Help clients know when to retry
Practical Exercise: API Design with HTTP Components
Task: Design a RESTful API for a Blog Platform
For this exercise, you'll design a RESTful API for a blog platform, focusing on the proper use of HTTP methods, headers, and status codes. The blog platform should support posts, comments, and user management.
Requirements:
- Design endpoints for the following resources:
- Posts (create, read, update, delete)
- Comments (create, read, update, delete)
- Users (create, read, update, delete)
- For each endpoint, specify:
- HTTP method
- URL pattern
- Required and optional headers
- Request body format (if applicable)
- Response status codes for success and error scenarios
- Response body format
- Implement at least one example of each of the following:
- Pagination
- Filtering
- Sorting
- Error handling
- Authentication
- Include appropriate caching and security headers
Deliverable:
Create a document or diagram detailing your API design. For at least two endpoints, provide complete request and response examples, including headers and bodies.
Example to get you started:
Endpoint: Get All Posts
- Method: GET
- URL: /api/posts
- Query Parameters:
- page (optional): Page number for pagination (default: 1)
- limit (optional): Number of posts per page (default: 10)
- sort (optional): Field to sort by (default: 'createdAt')
- order (optional): 'asc' or 'desc' (default: 'desc')
- category (optional): Filter by category
- Required Headers:
- None
- Optional Headers:
- Authorization: Bearer token for personalized content
- Success Response:
- Status Code: 200 OK
- Body:
{
"posts": [...],
"total": 42,
"page": 1,
"limit": 10,
"totalPages": 5
}
- Error Responses:
- Status Code: 400 Bad Request (invalid query parameters)
- Status Code: 401 Unauthorized (invalid or expired token)
- Status Code: 500 Internal Server Error
- Response Headers:
- Content-Type: application/json
- Cache-Control: max-age=60
Summary
In this session, we've explored the core components of HTTP in depth:
- HTTP Methods: The verbs that define what action to take on a resource (GET, POST, PUT, PATCH, DELETE)
- HTTP Headers: The metadata that provides additional information about requests and responses
- HTTP Status Codes: The numeric codes that indicate the result of a request
Understanding these components is crucial for building effective web applications and APIs. By using the right method, including appropriate headers, and returning meaningful status codes, you can create interfaces that are:
- Intuitive and easy to use
- Efficient in transmitting data
- Secure against common vulnerabilities
- Cacheable for performance optimization
- Clear about success and error conditions
In our next session, we'll build on this knowledge to explore RESTful API design principles, examining how to structure resources, handle relationships, and implement common patterns for web APIs.
Additional Resources
- MDN: HTTP Methods - Detailed documentation of HTTP methods
- MDN: HTTP Headers - Complete reference for HTTP headers
- MDN: HTTP Status Codes - Comprehensive list of status codes
- HTTP Statuses - Simple reference for HTTP status codes
- RFC 7231 - The official specification for HTTP/1.1 semantics
- Security Headers - Tool to analyze security headers on websites