All Posts

How to Add Security Headers to Your Next.js App

February 20, 20263 min readVibeSafe Team
nextjssecurity-headersvercelCSP

Why Security Headers Matter

Security headers are HTTP response headers that tell the browser how to handle your page. Without them, your app is vulnerable to:

  • XSS (Cross-Site Scripting) — attackers inject scripts that steal user data
  • Clickjacking — your app is embedded in a hidden iframe to trick users
  • MIME sniffing — browsers misinterpret file types and execute malicious content
  • Downgrade attacks — traffic is intercepted by forcing HTTP instead of HTTPS

Most AI coding tools don't add any security headers. This is the most common finding in VibeSafe scans — about 70% of scanned apps are missing critical headers.

The Headers You Need

Here's the complete list, in order of importance:

1. Strict-Transport-Security (HSTS)

Forces browsers to always use HTTPS. Prevents downgrade attacks.

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

2. X-Content-Type-Options

Prevents browsers from MIME-sniffing — stops them from treating a text file as JavaScript.

X-Content-Type-Options: nosniff

3. X-Frame-Options

Prevents your site from being embedded in iframes. Stops clickjacking attacks.

X-Frame-Options: DENY

4. Content-Security-Policy (CSP)

The most powerful security header. Controls which scripts, styles, images, and connections are allowed.

Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; connect-src 'self' https://*.supabase.co

5. Referrer-Policy

Controls how much URL information is sent when navigating to another site.

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

6. Permissions-Policy

Disables browser features you don't use (camera, microphone, geolocation).

Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=(self)

Adding Headers in Next.js

Method 1: next.config.ts (Recommended)

// next.config.ts
const securityHeaders = [
  { key: "Strict-Transport-Security", value: "max-age=63072000; includeSubDomains; preload" },
  { key: "X-Frame-Options", value: "DENY" },
  { key: "X-Content-Type-Options", value: "nosniff" },
  { key: "Referrer-Policy", value: "strict-origin-when-cross-origin" },
  { key: "Permissions-Policy", value: "camera=(), microphone=(), geolocation=(), payment=(self)" },
];

const nextConfig = {
  async headers() {
    return [{ source: "/(.*)", headers: securityHeaders }];
  },
};

export default nextConfig;

Method 2: Middleware (For Dynamic CSP)

Use middleware when you need nonce-based CSP for inline scripts:

// middleware.ts
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
import crypto from "crypto";

export function middleware(req: NextRequest) {
  const nonce = crypto.randomBytes(16).toString("base64");
  const res = NextResponse.next();

  res.headers.set(
    "Content-Security-Policy",
    `default-src 'self'; script-src 'self' 'nonce-${nonce}'; style-src 'self' 'unsafe-inline';`
  );

  return res;
}

Verifying Your Headers

After deploying, verify your headers are working:

  1. Browser DevTools — Network tab → click any request → check Response Headers
  2. curlcurl -I https://yourapp.com
  3. VibeSafeScan your URL for a full security header audit

Common Mistakes

Using unsafe-inline in CSP

AI tools often set script-src 'unsafe-inline' which defeats most of CSP's protection. Use nonce-based CSP instead when possible.

Setting HSTS max-age too low

A max-age of 300 (5 minutes) provides almost no protection. Use at least 31536000 (1 year), ideally 63072000 (2 years).

Forgetting about subdomains

If your app uses subdomains (api.yourapp.com, cdn.yourapp.com), add includeSubDomains to your HSTS header.

Testing Header Grade

After adding headers, run a VibeSafe scan to verify everything is configured correctly. Missing or misconfigured headers account for most "medium" severity findings in our reports.

Is your app vulnerable?

Run a free security scan and find out in 60 seconds.

Scan Your App Free