Express HTTP Headers
In this lesson, we'll explore HTTP headers in Express.js applications and how they play a crucial role in security, performance, and overall functionality of your web applications.
Introduction to HTTP Headers
HTTP headers are key-value pairs sent between a client and server during HTTP requests and responses. They provide important metadata about the request or response, allowing the client and server to pass additional information beyond the actual content.
Headers serve several purposes:
- Security: Protecting against various attacks
- Performance: Controlling caching behavior
- Authentication: Managing user sessions
- Content negotiation: Determining what type of content is sent
Express.js provides convenient ways to work with these headers to enhance your application's security and functionality.
Basic Header Manipulation in Express
Setting Headers
Express allows you to easily set response headers using the res.set()
method or the res.header()
alias:
app.get('/example', (req, res) => {
// Setting a single header
res.set('Content-Type', 'text/html');
// Setting multiple headers at once
res.set({
'Content-Type': 'text/html',
'Cache-Control': 'no-cache',
'X-Powered-By': 'Express'
});
res.send('<h1>Hello World!</h1>');
});
Reading Request Headers
You can access headers sent by clients through the req.headers
object:
app.get('/show-headers', (req, res) => {
console.log(req.headers);
// For example, accessing the user agent
const userAgent = req.headers['user-agent'];
res.send(`Your browser: ${userAgent}`);
});
Important Security Headers
1. Helmet.js: The Security Header Package
The simplest way to set secure HTTP headers is to use the helmet
package, which sets various security headers at once:
const express = require('express');
const helmet = require('helmet');
const app = express();
// Apply helmet middleware
app.use(helmet());
app.get('/', (req, res) => {
res.send('This response has secure headers!');
});
app.listen(3000);
Installing helmet:
npm install helmet
2. Key Security Headers and Their Purpose
Let's explore some important security headers and how to set them manually:
Content-Security-Policy (CSP)
CSP helps prevent Cross-Site Scripting (XSS) attacks by specifying which sources of content are trusted:
app.use((req, res, next) => {
res.set('Content-Security-Policy', "default-src 'self'; script-src 'self'");
next();
});
This policy restricts resources to be loaded only from your own domain.
X-XSS-Protection
This header enables browser's built-in XSS filtering:
app.use((req, res, next) => {
res.set('X-XSS-Protection', '1; mode=block');
next();
});
X-Frame-Options
Prevents your site from being embedded in iframes on other sites, protecting against clickjacking:
app.use((req, res, next) => {
res.set('X-Frame-Options', 'SAMEORIGIN');
next();
});
Strict-Transport-Security (HSTS)
Forces browsers to use HTTPS for future visits:
app.use((req, res, next) => {
res.set(
'Strict-Transport-Security',
'max-age=31536000; includeSubDomains; preload'
);
next();
});
Performance and Caching Headers
Cache-Control
Controls how browsers and other intermediaries should cache responses:
// For content that should not be cached
app.get('/private-data', (req, res) => {
res.set('Cache-Control', 'no-store, max-age=0');
res.send({ sensitiveData: 'example' });
});
// For content that can be cached
app.get('/public-info', (req, res) => {
res.set('Cache-Control', 'public, max-age=86400'); // Cache for 1 day
res.send({ publicData: 'This can be cached' });
});
ETag
Provides a unique identifier for a specific version of a resource, allowing efficient caching:
app.get('/data', (req, res) => {
const data = { message: 'This is cacheable content' };
// Express can automatically generate and handle ETags
res.set('ETag', computeETag(data)); // You'd implement computeETag
res.send(data);
});
Express has built-in support for ETags which can be enabled:
app.set('etag', 'strong');
Creating a Custom Headers Middleware
You can create a middleware function to consistently apply headers across your application:
// Custom security headers middleware
const securityHeaders = (req, res, next) => {
// Remove X-Powered-By header
res.removeHeader('X-Powered-By');
// Set security headers
res.set({
'Content-Security-Policy': "default-src 'self'",
'X-Content-Type-Options': 'nosniff',
'X-Frame-Options': 'SAMEORIGIN',
'X-XSS-Protection': '1; mode=block'
});
next();
};
// Apply to all routes
app.use(securityHeaders);
// Or apply to specific routes
app.get('/secure-page', securityHeaders, (req, res) => {
res.send('This page has extra security headers');
});
Practical Example: Complete Express App with Secure Headers
Here's a more comprehensive example showing header usage in a real application:
const express = require('express');
const helmet = require('helmet');
const app = express();
const PORT = 3000;
// Basic security headers with helmet
app.use(helmet());
// Custom additional headers
app.use((req, res, next) => {
// Add custom security header
res.set('X-Custom-Security-Header', 'CustomValue');
// Set strict CORS policy
res.set('Access-Control-Allow-Origin', 'https://trusted-site.com');
next();
});
// Public routes with caching enabled
app.get('/api/public-data', (req, res) => {
res.set('Cache-Control', 'public, max-age=3600'); // Cache for 1 hour
res.json({ message: 'This is public data that can be cached' });
});
// Private routes with caching disabled
app.get('/api/user-data', (req, res) => {
res.set('Cache-Control', 'private, no-cache, no-store');
res.json({ userData: 'Sensitive user information' });
});
// Content type specific headers
app.get('/download/report', (req, res) => {
res.set({
'Content-Type': 'application/pdf',
'Content-Disposition': 'attachment; filename="report.pdf"'
});
// In a real app, you would send the file here
res.send('PDF content would go here');
});
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
Common Gotchas and Tips
-
Headers must be set before sending the response:
javascript// CORRECT:
res.set('Header-Name', 'value');
res.send('Response body');
// INCORRECT - Headers won't be sent:
res.send('Response body');
res.set('Header-Name', 'value'); // Too late! -
Case insensitivity: HTTP headers are case-insensitive, but conventionally written in kebab-case:
javascript// These are equivalent:
res.set('Content-Type', 'text/html');
res.set('content-type', 'text/html'); -
Test your headers: Use tools like securityheaders.com to validate your headers.
Summary
HTTP headers are an essential part of web applications that allow you to control security, performance, and functionality. In Express.js:
- Use
res.set()
orres.header()
to set response headers - Access request headers with
req.headers
- Utilize the
helmet
package for comprehensive security headers - Implement caching headers to optimize performance
- Create custom middleware to consistently apply headers across your application
Properly configured HTTP headers are a crucial part of building secure, efficient Express applications.
Additional Resources
- Express.js Documentation on Response Headers
- Helmet.js Documentation
- MDN Web Docs: HTTP headers
- OWASP Secure Headers Project
Exercises
- Create a middleware that logs all incoming request headers to the console.
- Implement a route that serves an image with the appropriate caching headers.
- Build a simple API that requires an API key in the headers and validates it before processing requests.
- Create a middleware that sets different security headers based on the environment (development vs. production).
- Use the
helmet
package but customize it to allow loading scripts from a CDN.
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)