· 13 min read
HTTP Fundamentals for Senior Frontend Developers
Master HTTP concepts, methods, status codes, headers, and performance optimizations that every senior frontend developer should know for technical interviews.
As a senior frontend developer, understanding HTTP is crucial for building performant, secure, and scalable web applications. This comprehensive guide covers everything you need to know for technical interviews and real-world development.
What is HTTP?
HTTP (HyperText Transfer Protocol) is the foundation of data communication on the web. It’s an application-layer protocol that defines how clients and servers communicate over the internet.
Key Characteristics:
- Stateless: Each request is independent
- Request-Response: Client sends request, server responds
- Text-based: Human-readable format
- Connectionless: No persistent connection between requests
HTTP Methods (Verbs)
Core Methods
GET
- Retrieves data from server
- Safe and repeatable (same result every time)
- Data in URL parameters
- Cacheable by default
Usage: Most common method for fetching data from APIs, loading web pages, and retrieving resources. Used for read-only operations that don’t modify server state.
// Fetch user data
const response = await fetch('/api/users/123');
const user = await response.json();
// Fetch with query parameters
const response = await fetch('/api/posts?category=tech&limit=10');
const posts = await response.json();
POST
- Submits data to server
- Not safe or repeatable (can have different results)
- Data in request body
- Not cacheable by default
Usage: Used for creating new resources, submitting forms, file uploads, and any operation that modifies server state. Each request can have different effects, making it non-repeatable.
// Create a new user
const response = await fetch('/api/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: 'John Doe', email: 'john@example.com' }),
});
// Submit form data
const formData = new FormData();
formData.append('title', 'My Post');
formData.append('content', 'Post content here');
const response = await fetch('/api/posts', { method: 'POST', body: formData });
PUT
- Creates or updates entire resource
- Repeatable (same result every time) but not safe
- Replaces entire resource
Usage: Used for complete resource updates or creation when you know the exact resource identifier. The entire resource is replaced with the provided data. Safe to retry as it produces the same result.
// Update entire user profile
const response = await fetch('/api/users/123', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ id: 123, name: 'John Smith', email: 'johnsmith@example.com', role: 'admin' }),
});
// Create or replace a specific resource
const response = await fetch('/api/settings/theme', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ theme: 'dark', fontSize: 'large' }),
});
PATCH
- Partial updates to resource
- Not safe or repeatable (can have different results)
- Modifies specific fields
Usage: Used for partial updates when you only want to modify specific fields of a resource. More efficient than PUT for small changes. Not safe to retry as it can have different effects depending on current state.
// Update only user's email
const response = await fetch('/api/users/123', {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email: 'newemail@example.com' }),
});
// Update multiple specific fields
const response = await fetch('/api/posts/456', {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ title: 'Updated Title', published: true }),
});
DELETE
- Removes resource
- Repeatable (same result every time) but not safe
- May return 204 (No Content)
Usage: Used to remove resources from the server. Safe to retry as deleting an already deleted resource has the same effect. Often returns 204 No Content on success.
// Delete a user
const response = await fetch('/api/users/123', { method: 'DELETE' });
// Delete with confirmation
const response = await fetch('/api/posts/456', { method: 'DELETE', headers: { Authorization: 'Bearer ' + token } });
if (response.status === 204) {
console.log('Resource deleted successfully');
}
Additional Methods
HEAD
- Like GET but returns only headers
- Useful for checking resource existence
- No response body
Usage: Used to check if a resource exists, get metadata, or verify last modification time without downloading the actual content. Saves bandwidth when you only need header information.
// Check if a file exists
const response = await fetch('/api/files/document.pdf', { method: 'HEAD' });
if (response.ok) {
const lastModified = response.headers.get('Last-Modified');
const contentLength = response.headers.get('Content-Length');
console.log(`File exists, last modified: ${lastModified}, size: ${contentLength}`);
}
// Check resource availability
const response = await fetch('/api/status', { method: 'HEAD' });
const isAvailable = response.status === 200;
OPTIONS
- Returns allowed methods for resource
- Used for CORS preflight requests
Usage: Used for CORS preflight requests when making cross-origin requests with certain methods or headers. Also used to discover what operations are allowed on a resource. Browsers automatically send this before certain requests.
// CORS preflight (browser sends this automatically)
const response = await fetch('/api/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json', Authorization: 'Bearer token' },
body: JSON.stringify({ name: 'John' }),
});
// Check allowed methods for a resource
const response = await fetch('/api/users/123', { method: 'OPTIONS' });
const allowedMethods = response.headers.get('Allow');
console.log('Allowed methods:', allowedMethods); // "GET, PUT, DELETE"
HTTP Status Codes
1xx Informational
- 100 Continue: Client should continue request
- 101 Switching Protocols: Upgrading to different protocol
2xx Success
- 200 OK: Request successful
- 201 Created: Resource created successfully
- 202 Accepted: Request accepted but not processed
- 204 No Content: Success but no content returned
- 206 Partial Content: Partial content returned (useful for range requests)
3xx Redirection
- 301 Moved Permanently: Resource permanently moved
- 302 Found: Resource temporarily moved
- 304 Not Modified: Resource not changed (caching)
- 307 Temporary Redirect: Temporary redirect preserving method
- 308 Permanent Redirect: Permanent redirect preserving method
4xx Client Error
- 400 Bad Request: Malformed request
- 401 Unauthorized: Authentication required
- 403 Forbidden: Access denied
- 404 Not Found: Resource doesn’t exist
- 405 Method Not Allowed: HTTP method not supported
- 408 Request Timeout: Request took too long
- 409 Conflict: Resource conflict
- 413 Payload Too Large: Request too large
- 429 Too Many Requests: Rate limiting
5xx Server Error
- 500 Internal Server Error: Generic server error
- 501 Not Implemented: Method not implemented
- 502 Bad Gateway: Invalid response from upstream
- 503 Service Unavailable: Server temporarily unavailable
- 504 Gateway Timeout: Upstream server timeout
HTTP Headers
HTTP headers are key-value pairs that provide additional information about the request or response. They contain metadata that helps clients and servers understand how to process the communication. Headers are case-insensitive and can be used for authentication, content negotiation, caching, security, and more.
Request Headers
Authentication
Authorization: Bearer <token>
Authorization: Basic <base64-encoded-credentials>
Content Negotiation
Content negotiation allows the client to tell the server what format, language, or encoding it prefers for the response. The server then responds with the best match it can provide.
Accept: application/json, text/html
Accept-Language: en-US, en
Accept-Encoding: gzip, deflate, br
Caching
Request caching headers tell the server about cached resources the client already has, allowing the server to decide if it should send the full response or just a “not modified” status.
Cache-Control: no-cache, max-age=3600
If-Modified-Since: Wed, 21 Oct 2015 07:28:00 GMT
If-None-Match: "abc123"
CORS
Origin: https://example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Content-Type
Response Headers
Caching
Response caching headers tell the client how long to cache the resource and provide validation tokens to check if the resource has changed.
Cache-Control: public, max-age=3600
ETag: "abc123"
Last-Modified: Wed, 21 Oct 2015 07:28:00 GMT
CORS
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
Security
Content-Security-Policy: default-src 'self'
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
Strict-Transport-Security: max-age=31536000
HTTP/2 vs HTTP/1.1
HTTP/1.1 Limitations
- One request per connection (without pipelining)
- Headers sent with every request
- No server push capability
- Head-of-line blocking
HTTP/2 Improvements
- Multiplexing: Multiple requests over single connection
- Header Compression: HPACK algorithm reduces overhead
- Server Push: Server can send resources proactively
- Binary Protocol: More efficient than text-based
- Stream Prioritization: Important resources load first
Multiplexing
Allows multiple requests and responses to be sent simultaneously over a single TCP connection, eliminating the need for multiple connections.
HTTP/1.1 Problem:
Connection 1: GET /api/users → Response 1
Connection 2: GET /api/posts → Response 2
Connection 3: GET /api/comments → Response 3
HTTP/2 Solution:
Single Connection:
Stream 1: GET /api/users → Response 1
Stream 2: GET /api/posts → Response 2
Stream 3: GET /api/comments → Response 3
Header Compression (HPACK)
Reduces header size by using a compression table that stores previously sent headers.
Example:
First Request:
GET /api/users HTTP/2
Host: api.example.com
Authorization: Bearer abc123
Content-Type: application/json
Second Request (compressed):
GET /api/posts HTTP/2
:authority: api.example.com // Compressed Host
:authorization: Bearer abc123 // Compressed Authorization
Server Push
Server can send resources to the client before they’re requested, reducing round trips.
Example:
// Client requests HTML
GET /index.html HTTP/2
// Server responds with HTML AND pushes CSS/JS
HTTP/2 200 OK
// Pushes: styles.css, app.js, logo.png
Binary Protocol
Uses binary frames instead of text, making parsing faster and more efficient.
HTTP/1.1 (Text):
GET /api/users HTTP/1.1\r\n
Host: api.example.com\r\n
Authorization: Bearer token\r\n
\r\n
HTTP/2 (Binary Frames):
[Frame Header][Frame Payload]
[Type: HEADERS][Length: 50][Stream: 1]
[Compressed Headers Data]
Stream Prioritization
Allows setting priority levels for different resources, ensuring critical content loads first.
Example:
// High priority: Critical CSS
GET /critical.css HTTP/2
Priority: weight=256, exclusive=true
// Medium priority: Images
GET /hero-image.jpg HTTP/2
Priority: weight=128, depends=1
// Low priority: Analytics
GET /analytics.js HTTP/2
Priority: weight=32, depends=1
Performance Optimization
Caching Strategies
Browser Caching
// Cache-Control headers
Cache-Control: public, max-age=31536000 // 1 year
Cache-Control: private, max-age=3600 // 1 hour
Cache-Control: no-cache // Always validate
Cache-Control: no-store // Never cache
ETags and Conditional Requests
// Server sends ETag
ETag: "abc123"
// Client sends conditional request
If-None-Match: "abc123"
// Server responds with 304 if unchanged
Compression
- Gzip: Most common, good compression ratio
- Brotli: Better compression than gzip
- Deflate: Less common, similar to gzip
Connection Optimization
- Keep-Alive: Reuse connections
- HTTP/2: Multiplexing reduces connection overhead
- CDN: Geographic distribution of resources
HTTP/3
HTTP/3 is the latest version of HTTP, built on top of QUIC protocol instead of TCP. It provides significant improvements over HTTP/2.
Key Features
QUIC Protocol
- Built on UDP instead of TCP
- Faster connection establishment
- Better performance over unreliable networks
- Built-in encryption
Improved Performance
- Faster Handshakes: 0-RTT connection resumption
- Better Multiplexing: No head-of-line blocking
- Connection Migration: Seamless network switching
- Improved Congestion Control: Better handling of packet loss
Example:
// HTTP/3 is automatically used when supported
fetch('https://example.com/api/data')
.then((response) => response.json())
.then((data) => console.log(data));
// Check if HTTP/3 is supported
if ('connection' in navigator) {
const connection = navigator.connection;
console.log('Protocol:', connection.effectiveType);
}
WebSockets
WebSockets provide full-duplex communication between client and server over a single TCP connection, enabling real-time data exchange.
Key Characteristics
Persistent Connection
- Single TCP connection
- Low latency communication
- Real-time bidirectional data flow
- No HTTP overhead after handshake
Use Cases
- Real-time chat applications
- Live notifications
- Collaborative editing
- Live data feeds
- Gaming applications
WebSocket API
Server-Side Implementation (Node.js with ws library):
const WebSocket = require('ws');
const http = require('http');
// Create HTTP server
const server = http.createServer();
// Create WebSocket server
const wss = new WebSocket.Server({ server });
// Handle WebSocket connections
wss.on('connection', function connection(ws, request) {
console.log('New client connected');
// Send welcome message
ws.send(JSON.stringify({ type: 'welcome', message: 'Connected to WebSocket server', timestamp: Date.now() }));
// Handle incoming messages
ws.on('message', function incoming(data) {
try {
const message = JSON.parse(data);
console.log('Received:', message);
// Echo message back to client
ws.send(JSON.stringify({ type: 'echo', originalMessage: message, timestamp: Date.now() }));
// Broadcast to all connected clients
wss.clients.forEach(function each(client) {
if (client !== ws && client.readyState === WebSocket.OPEN) {
client.send(JSON.stringify({ type: 'broadcast', message: message, timestamp: Date.now() }));
}
});
} catch (error) {
console.error('Error parsing message:', error);
ws.send(JSON.stringify({ type: 'error', message: 'Invalid JSON format' }));
}
});
// Handle client disconnect
ws.on('close', function close() {
console.log('Client disconnected');
});
// Handle errors
ws.on('error', function error(err) {
console.error('WebSocket error:', err);
});
});
// Start server
server.listen(8080, function () {
console.log('WebSocket server running on port 8080');
});
Client-Side Connection:
// Create WebSocket connection
const socket = new WebSocket('wss://api.example.com/ws');
// Connection opened
socket.onopen = function (event) {
console.log('Connected to server');
socket.send('Hello Server!');
};
// Message received
socket.onmessage = function (event) {
console.log('Message from server:', event.data);
const data = JSON.parse(event.data);
// Handle real-time data
};
// Connection closed
socket.onclose = function (event) {
console.log('Connection closed:', event.code, event.reason);
};
// Error occurred
socket.onerror = function (error) {
console.error('WebSocket error:', error);
};
Sending Data:
// Send text message
socket.send('Hello Server!');
// Send JSON data
socket.send(JSON.stringify({ type: 'message', content: 'Hello World', timestamp: Date.now() }));
// Send binary data
const buffer = new ArrayBuffer(8);
socket.send(buffer);
Connection States:
// Check connection state
if (socket.readyState === WebSocket.OPEN) {
socket.send('Data');
}
// States:
// WebSocket.CONNECTING = 0
// WebSocket.OPEN = 1
// WebSocket.CLOSING = 2
// WebSocket.CLOSED = 3
WebSocket vs HTTP
HTTP (Request-Response)
- Stateless communication
- Client initiates requests
- Server responds to requests
- Higher overhead per message
WebSocket (Bidirectional)
- Persistent connection
- Server can push data anytime
- Lower overhead per message
- Real-time communication
When to Use WebSockets:
- Real-time applications (chat, notifications)
- Live data feeds (stock prices, sports scores)
- Collaborative features (shared editing)
- Gaming applications
- Live streaming
When to Use HTTP:
- Traditional web applications
- RESTful APIs
- File uploads/downloads
- One-time data requests
- Caching requirements
Security Considerations
HTTPS
- TLS/SSL: Encrypts data in transit
- Certificate Validation: Ensures server identity
- HSTS: Forces HTTPS connections
CORS (Cross-Origin Resource Sharing)
CORS is a security feature that allows web pages to make requests to different domains. By default, browsers block cross-origin requests for security reasons.
What is “Cross-Origin”?
- Same origin:
https://myapp.com
→https://myapp.com/api
✅ - Cross-origin:
https://myapp.com
→https://api.example.com
❌ (blocked by default)
How CORS Works:
- Browser checks if request is cross-origin
- For simple requests: Browser sends request directly
- For complex requests: Browser sends OPTIONS preflight request first
- Server responds with CORS headers allowing/denying the request
Example:
// SIMPLE REQUEST - Browser sends directly
// Only GET, HEAD, POST with basic headers
fetch('https://api.example.com/data');
// COMPLEX REQUEST - Browser sends OPTIONS preflight first
// Uses custom headers, methods other than GET/POST/HEAD
fetch('https://api.example.com/data', {
method: 'POST',
headers: { 'Content-Type': 'application/json', Authorization: 'Bearer token' },
body: JSON.stringify({ name: 'John' }),
});
// What happens behind the scenes:
// 1. Browser sends: OPTIONS /data (preflight)
// 2. Server responds: Access-Control-Allow-Origin: https://myapp.com
// 3. Browser sends: POST /data (actual request)
// 4. Server responds: Your data
Server Response Headers:
Access-Control-Allow-Origin: https://myapp.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
Security Headers
// Prevent clickjacking
X-Frame-Options: DENY
// Prevent MIME type sniffing
X-Content-Type-Options: nosniff
// XSS protection
X-XSS-Protection: 1; mode=block
// Content Security Policy
Content-Security-Policy: default-src 'self'
Common Interview Questions
1. What’s the difference between GET and POST?
- GET: Retrieves data, repeatable, cacheable, data in URL
- POST: Submits data, not repeatable, not cacheable, data in body
2. Explain HTTP status codes 200, 201, 204, 400, 401, 403, 404, 500
- 200: Success
- 201: Created
- 204: No Content
- 400: Bad Request
- 401: Unauthorized
- 403: Forbidden
- 404: Not Found
- 500: Internal Server Error
3. What is CORS and how does it work?
CORS allows web pages to make requests to different domains. Browser enforces same-origin policy, but CORS headers allow cross-origin requests.
4. Explain HTTP caching mechanisms
- Browser Cache: Stores resources locally
- Cache-Control: Controls caching behavior
- ETags: Validates if resource changed
- Last-Modified: Time-based validation
5. What are the benefits of HTTP/2?
- Multiplexing
- Header compression
- Server push
- Binary protocol
- Stream prioritization
6. Why don’t we only use GET, POST, and DELETE? Why do we need PUT, PATCH, HEAD, and OPTIONS?
While it’s technically possible to use only GET, POST, and DELETE, having specific methods provides several important benefits:
Semantic Clarity
- Each method clearly communicates intent
PUT
vsPATCH
tells you if it’s a complete or partial updateHEAD
vsGET
indicates if you need the response body
HTTP Standards Compliance
- RESTful APIs follow HTTP standards for better interoperability
- Caching works correctly (GET is cacheable, POST isn’t)
- Browsers and proxies understand the semantics
Performance & Efficiency
HEAD
saves bandwidth when you only need headersPATCH
reduces payload size for partial updatesOPTIONS
enables CORS preflight without full requests
Safety & Reliability
GET
andHEAD
are safe (no side effects)PUT
andDELETE
are repeatable (same result when retried)POST
andPATCH
are not repeatable (prevents accidental duplicates)
Example of why PUT ≠ PATCH:
// PUT - replaces entire resource
PUT /api/users/123
{ "id": 123, "name": "John", "email": "john@example.com", "role": "admin" }
// PATCH - updates only specific fields
PATCH /api/users/123
{ "email": "newemail@example.com" }
Using only GET/POST/DELETE would require encoding this information in the URL or request body, making APIs less intuitive and harder to cache properly.
Practical Examples
Fetch API with Error Handling
async function fetchData(url) {
try {
const response = await fetch(url, {
method: 'GET',
headers: { Accept: 'application/json', 'Cache-Control': 'no-cache' },
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
console.error('Fetch error:', error);
throw error;
}
}
Handling Different Response Types
async function handleResponse(response) {
const contentType = response.headers.get('content-type');
if (contentType?.includes('application/json')) {
return await response.json();
} else if (contentType?.includes('text/')) {
return await response.text();
} else {
return await response.blob();
}
}
Implementing Retry Logic
async function fetchWithRetry(url, options = {}, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
const response = await fetch(url, options);
if (response.ok) return response;
if (response.status >= 500 && i < maxRetries - 1) {
await new Promise((resolve) => setTimeout(resolve, 1000 * Math.pow(2, i)));
continue;
}
throw new Error(`HTTP ${response.status}`);
} catch (error) {
if (i === maxRetries - 1) throw error;
await new Promise((resolve) => setTimeout(resolve, 1000 * Math.pow(2, i)));
}
}
}
Conclusion
Mastering HTTP is essential for senior frontend developers. Understanding methods, status codes, headers, caching, and security concepts will help you build better applications and excel in technical interviews. Focus on practical implementation and real-world scenarios to demonstrate your expertise.
Remember, HTTP knowledge extends beyond basic concepts - it’s about understanding how the web works at its core and applying that knowledge to solve complex problems efficiently.