Express Request Headers
Introduction
HTTP headers are an essential part of the communication between clients and servers on the web. They contain important metadata about the request or response such as content type, authentication credentials, caching directives, and more. In Express.js applications, being able to access and manipulate request headers is a fundamental skill for building robust web applications.
This guide will walk you through everything you need to know about working with request headers in Express.js, from basic access to advanced usage patterns.
Understanding HTTP Headers
Before diving into Express-specific code, let's understand what HTTP headers are:
HTTP headers are simple key-value pairs sent at the beginning of HTTP requests and responses. They define the operating parameters of an HTTP transaction.
Common request headers include:
Content-Type
: Indicates the media type of the resourceAuthorization
: Contains credentials for authenticating a userUser-Agent
: Identifies the client application or browserAccept
: Specifies which content types the client can processCookie
: Contains stored HTTP cookies
Accessing Request Headers in Express
Express provides a simple and intuitive way to access request headers through the req.headers
object.
Basic Header Access
const express = require('express');
const app = express();
app.get('/headers', (req, res) => {
// Access all headers
const allHeaders = req.headers;
// Access a specific header
const userAgent = req.headers['user-agent'];
res.send({
allHeaders,
userAgent
});
});
app.listen(3000, () => {
console.log('Server running on port 3000');
});
When you make a request to /headers
, you would see a response containing all the headers your browser sent, plus the specific User-Agent header value.
Case-Insensitivity of Headers
An important thing to note is that header names in Express are case-insensitive. Express normalizes all header names to lowercase:
app.get('/case-test', (req, res) => {
// All of these will access the same header
const contentType1 = req.headers['content-type'];
const contentType2 = req.headers['Content-Type'];
const contentType3 = req.headers['CONTENT-TYPE'];
res.send({
contentType1,
contentType2,
contentType3
});
});
All three variables will contain the same value because Express converts header names to lowercase.
Convenience Methods for Common Headers
Express provides convenience methods for accessing some commonly used headers:
The req.get()
Method
app.get('/get-header', (req, res) => {
// Using the req.get() method
const contentType = req.get('Content-Type');
const accept = req.get('Accept');
res.send({
contentType,
accept
});
});
The req.get()
method is case-insensitive and provides a cleaner way to access headers compared to directly using req.headers
.
Working with Specific Headers
Let's explore how to work with some commonly used headers:
Content-Type Header
The Content-Type header tells the server what kind of data is being sent:
app.post('/api/data', (req, res) => {
const contentType = req.get('Content-Type');
if (contentType !== 'application/json') {
return res.status(415).send('Unsupported Media Type - Please send JSON');
}
// Process the JSON data
res.send('JSON data received successfully');
});
Authorization Header
The Authorization header is used for authenticating users:
app.get('/protected', (req, res) => {
const authHeader = req.get('Authorization');
if (!authHeader) {
return res.status(401).send('Authentication required');
}
// Basic Auth example
if (authHeader.startsWith('Basic ')) {
const base64Credentials = authHeader.slice('Basic '.length);
const credentials = Buffer.from(base64Credentials, 'base64').toString('ascii');
const [username, password] = credentials.split(':');
if (username === 'admin' && password === 'password') {
return res.send('Welcome, admin!');
}
}
res.status(401).send('Invalid credentials');
});
Accept Header
The Accept header specifies which content types the client can understand:
app.get('/content', (req, res) => {
const acceptHeader = req.get('Accept');
if (acceptHeader.includes('application/json')) {
return res.json({ message: 'Here is some JSON data' });
}
if (acceptHeader.includes('text/html')) {
return res.send('<h1>Here is some HTML content</h1>');
}
res.send('Plain text response as fallback');
});
User-Agent Header
The User-Agent header helps identify the client:
app.get('/detect-client', (req, res) => {
const userAgent = req.get('User-Agent');
let clientInfo = 'Unknown client';
if (userAgent.includes('Mozilla')) {
clientInfo = 'Browser';
if (userAgent.includes('Chrome')) {
clientInfo = 'Chrome browser';
} else if (userAgent.includes('Firefox')) {
clientInfo = 'Firefox browser';
}
} else if (userAgent.includes('PostmanRuntime')) {
clientInfo = 'Postman';
} else if (userAgent.includes('curl')) {
clientInfo = 'curl command-line tool';
}
res.send(`You are using: ${clientInfo}`);
});
Real-world Applications
Let's look at some practical applications of handling request headers in Express:
Content Negotiation
Content negotiation allows the server to serve different formats based on what the client can accept:
app.get('/api/products/:id', (req, res) => {
const productId = req.params.id;
const product = {
id: productId,
name: 'Smartphone',
price: 599.99,
description: 'Latest model with advanced features'
};
const acceptHeader = req.get('Accept');
if (acceptHeader.includes('application/json')) {
return res.json(product);
}
if (acceptHeader.includes('application/xml')) {
const xml = `
<product>
<id>${product.id}</id>
<name>${product.name}</name>
<price>${product.price}</price>
<description>${product.description}</description>
</product>
`;
res.type('application/xml');
return res.send(xml);
}
// Default to plain text
res.type('text/plain');
res.send(`Product: ${product.name}, Price: $${product.price}`);
});
API Versioning
Headers can be used to handle API versioning:
app.get('/api/features', (req, res) => {
const apiVersion = req.get('X-API-Version') || '1';
if (apiVersion === '1') {
return res.json({
features: ['basic search', 'user profiles']
});
}
if (apiVersion === '2') {
return res.json({
features: ['advanced search', 'user profiles', 'real-time notifications', 'social sharing']
});
}
res.status(400).json({ error: 'Unsupported API version' });
});
Caching and Conditional Requests
Headers like If-Modified-Since
enable efficient caching:
const lastModified = new Date('2023-01-01').toUTCString();
app.get('/api/data', (req, res) => {
const ifModifiedSince = req.get('If-Modified-Since');
if (ifModifiedSince && new Date(ifModifiedSince) >= new Date(lastModified)) {
// Resource hasn't been modified
return res.status(304).end(); // 304 Not Modified
}
// Set the Last-Modified header
res.set('Last-Modified', lastModified);
res.json({
data: 'This is the actual data',
timestamp: new Date().toISOString()
});
});
Creating Custom Headers
You can also check for custom headers that your frontend or API clients might send:
app.get('/api/custom-header-demo', (req, res) => {
const apiKey = req.get('X-API-Key');
const clientId = req.get('X-Client-ID');
if (!apiKey) {
return res.status(400).json({ error: 'Missing API key header' });
}
// Log client information
console.log(`Request received from client: ${clientId || 'unknown'}, with API key: ${apiKey}`);
res.json({ success: true, message: 'Custom headers received' });
});
Middleware for Header Processing
For more complex applications, you might want to create middleware to process headers:
// Authentication middleware using headers
function authMiddleware(req, res, next) {
const apiKey = req.get('X-API-Key');
if (!apiKey) {
return res.status(401).json({ error: 'API key required' });
}
// Validate API key (in a real app, you'd check against a database)
if (apiKey !== 'your-valid-api-key') {
return res.status(403).json({ error: 'Invalid API key' });
}
// Add user info to the request object for later middleware/routes
req.user = { authorized: true };
next();
}
// Use the middleware for protected routes
app.get('/api/protected-data', authMiddleware, (req, res) => {
res.json({ secretData: 'This is protected information' });
});
Security Considerations
When working with headers, keep these security aspects in mind:
app.use((req, res, next) => {
// Set security headers
res.set('X-Content-Type-Options', 'nosniff');
res.set('X-Frame-Options', 'DENY');
res.set('X-XSS-Protection', '1; mode=block');
next();
});
app.get('/secure-endpoint', (req, res) => {
// Validate Origin header for CORS requests
const origin = req.get('Origin');
const allowedOrigins = ['https://trusted-site.com', 'https://another-trusted-site.com'];
if (origin && !allowedOrigins.includes(origin)) {
console.log(`Rejected request from unauthorized origin: ${origin}`);
return res.status(403).send('Forbidden');
}
res.send('Secure data');
});
Summary
Express.js provides straightforward methods for working with HTTP request headers, allowing you to:
- Access all headers using the
req.headers
object - Get specific headers with
req.get('Header-Name')
orreq.headers['header-name']
- Use headers for authentication, content negotiation, and API versioning
- Create middleware to process headers consistently across routes
Headers are a fundamental aspect of HTTP communication and mastering them will help you build more sophisticated and robust web applications.
Additional Resources
Practice Exercises
- Create an Express endpoint that returns different responses based on the
Accept-Language
header. - Implement a rate-limiting middleware that uses the
X-Rate-Limit
header to inform clients about their remaining requests. - Build a simple authentication system using the
Authorization
header with JWT tokens. - Create a logging middleware that captures and logs important request headers for debugging purposes.
- Implement content negotiation for an API that can respond with JSON, XML, or CSV formats based on the
Accept
header.
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)