Skip to main content

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:

javascript
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:

javascript
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:

javascript
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:

bash
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:

javascript
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:

javascript
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:

javascript
app.use((req, res, next) => {
res.set('X-Frame-Options', 'SAMEORIGIN');
next();
});

Strict-Transport-Security (HSTS)

Forces browsers to use HTTPS for future visits:

javascript
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:

javascript
// 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:

javascript
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:

javascript
app.set('etag', 'strong');

Creating a Custom Headers Middleware

You can create a middleware function to consistently apply headers across your application:

javascript
// 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:

javascript
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

  1. 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!
  2. 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');
  3. 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() or res.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

Exercises

  1. Create a middleware that logs all incoming request headers to the console.
  2. Implement a route that serves an image with the appropriate caching headers.
  3. Build a simple API that requires an API key in the headers and validates it before processing requests.
  4. Create a middleware that sets different security headers based on the environment (development vs. production).
  5. 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! :)