Skip to main content
SecScannerSecScanner
Security ChecksFree ToolsPricingBlog
Get Started
Sign InGet Started
← Back to Blog
HeadersMarch 7, 202610 min read

How to Add Security Headers in Next.js (Complete Guide with Examples)

Learn how to configure all essential security headers in Next.js using next.config.ts. Copy-paste examples for HSTS, CSP, X-Frame-Options, Permissions-Policy, and cross-origin headers — with real-world edge cases like OAuth and OG images.

By SecScanner Team
How to Add Security Headers in Next.js (Complete Guide with Examples)

Next.js makes adding security headers straightforward — but most tutorials only cover the basics. This guide shows you how to configure every essential security header in Next.js, including real-world edge cases that break OAuth flows, OG image crawlers, and embedded content if you get them wrong.

We use Next.js to build SecScanner, so every example here comes from a production app that passes all 54 of our own security checks.

Quick Check: Does Your Next.js App Need Security Headers?

Run a free scan at secscanner.app — paste your URL and you'll see exactly which headers are missing in under 30 seconds. If you see red items for HSTS, X-Frame-Options, CSP, or Referrer-Policy, this guide is for you.

Where to Add Security Headers in Next.js

Next.js provides the headers() function in next.config.ts (or next.config.js). This is the recommended approach because:

  • Headers apply to all routes (pages, API routes, static files)
  • No middleware overhead — headers are set at the routing layer
  • You can use route patterns to apply different headers per path
  • Works with both Pages Router and App Router

The Essential Security Headers

Here's a production-ready next.config.ts with all critical security headers:

// next.config.ts
import type { NextConfig } from "next";

const nextConfig: NextConfig = {
  // Remove the X-Powered-By: Next.js header
  poweredByHeader: false,

  async headers() {
    return [
      {
        // Apply to all routes
        source: "/:path*",
        headers: [
          {
            key: "Strict-Transport-Security",
            value: "max-age=31536000; includeSubDomains; preload",
          },
          {
            key: "X-Content-Type-Options",
            value: "nosniff",
          },
          {
            key: "X-Frame-Options",
            value: "DENY",
          },
          {
            key: "Referrer-Policy",
            value: "strict-origin-when-cross-origin",
          },
          {
            key: "Permissions-Policy",
            value: "geolocation=(), microphone=(), camera=()",
          },
          {
            key: "Cross-Origin-Embedder-Policy",
            value: "credentialless",
          },
          {
            key: "Cross-Origin-Opener-Policy",
            value: "same-origin",
          },
          {
            key: "Cross-Origin-Resource-Policy",
            value: "same-origin",
          },
        ],
      },
    ];
  },
};

export default nextConfig;

Let's break down each header and why it matters.

1. Strict-Transport-Security (HSTS)

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

Forces browsers to always use HTTPS. Without this header, an attacker on a public Wi-Fi network can intercept the initial HTTP request before the redirect to HTTPS happens (an SSL stripping attack).

  • max-age=31536000 — remember for 1 year
  • includeSubDomains — apply to all subdomains too
  • preload — eligible for browser preload lists (hardcoded HTTPS)

Warning: Only add preload if you're certain all subdomains support HTTPS. Once submitted to the preload list, it's difficult to remove.

2. X-Content-Type-Options

X-Content-Type-Options: nosniff

Prevents browsers from MIME-type sniffing. Without it, a browser might interpret a CSS file as JavaScript or execute an uploaded image as a script — a classic attack vector for file upload vulnerabilities.

3. X-Frame-Options

X-Frame-Options: DENY

Prevents your site from being embedded in iframes. This blocks clickjacking attacks where an attacker overlays your page with invisible elements to trick users into clicking buttons they can't see.

Use SAMEORIGIN instead of DENY if your app uses iframes to embed its own pages.

4. Referrer-Policy

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

Controls how much URL information is shared when users navigate away from your site:

  • Same-origin requests: full URL is sent
  • Cross-origin requests: only the origin (domain) is sent
  • HTTPS→HTTP: nothing is sent

This prevents leaking sensitive URL paths (like /dashboard/user/12345) to external sites.

5. Permissions-Policy

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

Disables browser features your app doesn't use. If an XSS attack injects a script, it won't be able to access the user's camera or microphone because the policy blocks it at the browser level.

Common features to disable:

Permissions-Policy: geolocation=(), microphone=(), camera=(), payment=(), usb=(), magnetometer=(), gyroscope=(), accelerometer=()

Only list features you actually want to block. If your app uses geolocation, remove it from the list or set geolocation=(self).

6. Cross-Origin Headers (COOP, COEP, CORP)

These three headers work together to isolate your origin and enable powerful security features:

Cross-Origin-Embedder-Policy: credentialless
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Resource-Policy: same-origin
  • COEP (credentialless): Cross-origin resources load without credentials. Use credentialless instead of require-corp to avoid breaking third-party images and scripts that don't set CORP headers.
  • COOP (same-origin): Prevents other sites from getting a reference to your window (blocks Spectre-class attacks).
  • CORP (same-origin): Prevents other sites from loading your resources.

7. Remove X-Powered-By

// In next.config.ts
poweredByHeader: false,

Next.js adds X-Powered-By: Next.js by default. This reveals your tech stack to attackers. While security through obscurity isn't a defense strategy, there's no reason to advertise your framework.

Real-World Edge Cases

The headers above will break certain features if applied blindly. Here are the edge cases we've hit in production and how to fix them.

OAuth Login Breaks with COOP: same-origin

Cross-Origin-Opener-Policy: same-origin blocks the popup/redirect flow that Google OAuth (and other providers) use. The OAuth callback page can't communicate with the opener window.

Fix: Relax COOP specifically for auth routes:

async headers() {
  return [
    {
      source: "/:path*",
      headers: [
        // ... all security headers including COOP: same-origin
      ],
    },
    // Override COOP for OAuth callback routes
    {
      source: "/auth/:path*",
      headers: [
        {
          key: "Cross-Origin-Opener-Policy",
          value: "same-origin-allow-popups",
        },
      ],
    },
    {
      source: "/api/auth/:path*",
      headers: [
        {
          key: "Cross-Origin-Opener-Policy",
          value: "same-origin-allow-popups",
        },
      ],
    },
  ];
}

The more specific routes must come after the catch-all /:path* rule to override it.

OG Images Blocked by CORP: same-origin

If you use Next.js OG image generation (opengraph-image.tsx), social media crawlers (Twitter, Facebook, LinkedIn) can't fetch your OG images because Cross-Origin-Resource-Policy: same-origin blocks cross-origin requests.

Fix: Allow cross-origin access specifically for OG image routes:

{
  source: "/:path*/opengraph-image",
  headers: [
    {
      key: "Cross-Origin-Resource-Policy",
      value: "cross-origin",
    },
  ],
},
{
  source: "/opengraph-image",
  headers: [
    {
      key: "Cross-Origin-Resource-Policy",
      value: "cross-origin",
    },
  ],
}

Content-Security-Policy (CSP)

CSP is the most powerful — and most complex — security header. It's not included in the basic config above because a misconfigured CSP will break your app. Here's how to add it properly:

Step 1: Start with Report-Only mode

{
  key: "Content-Security-Policy-Report-Only",
  value: "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self'; connect-src 'self' https://your-analytics.com; report-uri /api/csp-report",
}

This logs violations without blocking anything. Monitor the reports for a week.

Step 2: Tighten and enforce

Once you've identified all legitimate sources, switch from Content-Security-Policy-Report-Only to Content-Security-Policy.

Common sources you'll need to whitelist in a Next.js app:

  • 'unsafe-inline' for styles (Tailwind, CSS-in-JS)
  • Your analytics domain (PostHog, Google Analytics)
  • Your CDN for images
  • Google Fonts if you use them

For a deeper CSP guide, see our Content Security Policy Masterclass.

Using Middleware Instead (Alternative Approach)

For dynamic CSP with nonces (needed for strict CSP without 'unsafe-inline'), you'll need Next.js middleware:

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

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

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

  return response;
}

This approach adds per-request overhead. Only use middleware if you need dynamic nonces — for most apps, the static next.config.ts approach is simpler and faster.

Verifying Your Headers

After deploying, verify your headers are working:

Option 1: curl

curl -I https://your-site.com

Check that all headers appear in the response.

Option 2: Browser DevTools

Open DevTools → Network tab → click on the document request → check Response Headers.

Option 3: SecScanner (Recommended)

Run a free scan at secscanner.app to check all 24 security configurations at once, including headers, TLS, and content security. The scan takes under 30 seconds and tells you exactly what's passing and what needs fixing.

Complete Production Example

Here's the full next.config.ts we use at SecScanner, handling all the edge cases discussed above:

import type { NextConfig } from "next";

const nextConfig: NextConfig = {
  poweredByHeader: false,

  async headers() {
    return [
      {
        source: "/:path*",
        headers: [
          {
            key: "Strict-Transport-Security",
            value: "max-age=31536000; includeSubDomains; preload",
          },
          {
            key: "X-Content-Type-Options",
            value: "nosniff",
          },
          {
            key: "X-Frame-Options",
            value: "DENY",
          },
          {
            key: "Referrer-Policy",
            value: "strict-origin-when-cross-origin",
          },
          {
            key: "Permissions-Policy",
            value: "geolocation=(), microphone=(), camera=()",
          },
          {
            key: "Cross-Origin-Embedder-Policy",
            value: "credentialless",
          },
          {
            key: "Cross-Origin-Opener-Policy",
            value: "same-origin",
          },
          {
            key: "Cross-Origin-Resource-Policy",
            value: "same-origin",
          },
        ],
      },
      // Relax COOP for OAuth flows
      {
        source: "/auth/:path*",
        headers: [
          {
            key: "Cross-Origin-Opener-Policy",
            value: "same-origin-allow-popups",
          },
        ],
      },
      {
        source: "/api/auth/:path*",
        headers: [
          {
            key: "Cross-Origin-Opener-Policy",
            value: "same-origin-allow-popups",
          },
        ],
      },
      // Allow OG images to be fetched by social crawlers
      {
        source: "/:path*/opengraph-image",
        headers: [
          {
            key: "Cross-Origin-Resource-Policy",
            value: "cross-origin",
          },
        ],
      },
    ];
  },
};

export default nextConfig;

Common Mistakes

  • Adding headers in middleware AND next.config.ts: They don't merge — one overwrites the other. Pick one approach and stick with it.
  • Forgetting poweredByHeader: false: Security headers are less effective if you're advertising your exact framework version.
  • Setting COOP: same-origin globally: This will break Google OAuth, Sign in with Apple, and any popup-based auth flow. Always create route-specific overrides for auth paths.
  • Using COEP: require-corp instead of credentialless: require-corp blocks all cross-origin resources that don't explicitly set CORP headers — this breaks most third-party images, fonts, and scripts. Use credentialless for a safer default.
  • Applying CSP without testing: Always start with Content-Security-Policy-Report-Only to avoid breaking your app in production.

Security Headers Checklist for Next.js

  • Set poweredByHeader: false in next.config.ts
  • Add HSTS with includeSubDomains and preload
  • Add X-Content-Type-Options: nosniff
  • Add X-Frame-Options: DENY (or SAMEORIGIN if you iframe yourself)
  • Add Referrer-Policy: strict-origin-when-cross-origin
  • Add Permissions-Policy disabling unused browser features
  • Add cross-origin headers (COEP, COOP, CORP) with auth route overrides
  • Start CSP in report-only mode, then enforce after testing
  • Override CORP to cross-origin for OG image routes
  • Run a SecScanner scan to verify all headers are correctly set

Security headers are the lowest-effort, highest-impact security improvement you can make to a Next.js app. The config above takes 5 minutes to add and protects against clickjacking, MIME sniffing, protocol downgrades, and cross-origin attacks. Scan your site now to see your current score.

Related Articles

Headers

The Complete Guide to HTTP Security Headers

10 min read

Headers

Content Security Policy Masterclass: From Basics to Advanced Protection

12 min read

TLS

HSTS Preload: Force HTTPS for Every Visitor from the First Connection

8 min read

Headers

Clickjacking Prevention: X-Frame-Options & CSP Guide

9 min read

Check Your Website Security

Want to see how your website measures up? Run a free security scan with SecScanner to identify vulnerabilities and get actionable remediation guidance.

Scan Your Website Free
Security Headers CheckerHeader Security Checks

On This Page

Product

  • Security Checks
  • Free Tools
  • SSL Checker
  • Vulnerability Scanner
  • Email Security
  • Pricing
  • Compliance
  • Security Reports

Popular Checks

  • CSP Check
  • HSTS Check
  • TLS Version Check
  • SSL Expiry Check
  • SPF/DKIM/DMARC Check
  • Cookie Security Check
  • JS Vulnerability Scan
  • OCSP Stapling Check

Resources

  • Blog
  • Glossary
  • Contact

Legal

  • Terms of Use
  • Privacy Policy
  • Refund Policy
  • Cookie Policy

© 2025-2026 SecScanner. All rights reserved.