DEV Community

Mario Herreros
Mario Herreros

Posted on

Strengthening Web Security with HTTP Headers in Express.js

When developing web applications, security should always be a top priority. A simple yet powerful way to protect your app and users is by configuring HTTP security headers. These headers instruct the browser on how to behave when handling content from your site, helping to mitigate common attacks like cross-site scripting (XSS), clickjacking, and content sniffing.

In this article, we’ll walk through an Express.js middleware that sets key HTTP headers, explain their purposes, and suggest additional headers you should consider for modern, robust protection.

An example of security header middleware

The following is an example of security header middleware:

const express = require('express');
const app = express();

app.use((req, res, next) => {
  res.set({
    'Strict-Transport-Security': 'max-age=63072000; preload',
    'X-Content-Type-Options': 'nosniff',
    'X-XSS-Protection': '1; mode=block',
    'Content-Security-Policy': "default-src 'self'; script-src 'self'; object-src 'none'; upgrade-insecure-requests",
    'Referrer-Policy': 'strict-origin-when-cross-origin',
    'Permissions-Policy': 'fullscreen=(self), camera=(), geolocation=()',
    'Cross-Origin-Embedder-Policy': 'require-corp',
    'Cross-Origin-Opener-Policy': 'same-origin',
    'Cross-Origin-Resource-Policy': 'same-origin'
  });
  next();
});

Enter fullscreen mode Exit fullscreen mode

What These Headers Do

1. Strict-Transport-Security

Strict-Transport-Security: max-age=63072000; preload

This tells browsers to only connect via HTTPS for the next 2 years (63072000 seconds). The preload directive allows your domain to be added to browsers' HSTS preload list, preventing even the first request from using insecure HTTP.

Why it matters: Protects against SSL stripping attacks by ensuring all future connections are encrypted.

2. X-Content-Type-Options

X-Content-Type-Options: nosniff

Prevents browsers from guessing (or "sniffing") the MIME type of a resource. This is important because misinterpreted files (e.g., JavaScript served as images) can be dangerous.

Why it matters: Reduces risk of executing malicious files disguised as safe content.

3. X-XSS-Protection

X-XSS-Protection: 1; mode=block

This enables the XSS protection filter in some older browsers. It tells the browser to block the page if an attack is detected.

Why it matters: Offers limited protection against cross-site scripting attacks in legacy environments.

4. Content-Security-Policy (CSP)

Content-Security-Policy: default-src 'self'

Purpose: Controls which resources (scripts, images, styles, etc.) can be loaded. Strong CSP rules help prevent XSS and data injection attacks.

5. Referrer-Policy

Referrer-Policy: strict-origin-when-cross-origin

Purpose: Limits what referrer information is shared when navigating away from your site, protecting user privacy and sensitive URLs.

6. Permissions-Policy (formerly Feature-Policy)

Permissions-Policy: fullscreen=(self), camera=(), geolocation=()

Purpose: Restricts browser APIs (like camera, geolocation, microphone, etc.) to only allowed origins or disables them entirely.

7. Cross-Origin-Embedder-Policy (COEP), Cross-Origin-Opener-Policy (COOP) & Cross-Origin-Resource-Policy (CORP)

These help isolate your site from other origins and protect against cross-origin attacks and speculative execution vulnerabilities (like Spectre).

Recommended configuration:

Cross-Origin-Embedder-Policy: require-corp
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Resource-Policy: same-origin
Enter fullscreen mode Exit fullscreen mode

Using helmet — the easier way

Instead of manually adding headers, you can use helmet, a middleware package that sets many of these headers by default:

npm install helmet
Enter fullscreen mode Exit fullscreen mode

Then in your app:

const helmet = require('helmet');
app.use(helmet());
Enter fullscreen mode Exit fullscreen mode

You can also configure each header individually:

app.use(helmet.contentSecurityPolicy({
  directives: {
    defaultSrc: ["'self'"],
    scriptSrc: ["'self'"]
  }
}));
Enter fullscreen mode Exit fullscreen mode

Conclusion

HTTP security headers are a simple but essential way to reduce your application’s attack surface. While no single measure guarantees complete protection, layering these headers with best practices (like input validation and HTTPS) significantly strengthens your web app’s defenses.

Start small, use tools like helmet, and review your security settings regularly—because protecting your users is not optional.

Top comments (0)