Back to Blog
Guide · 4 min read

Security Headers Every Web App Needs (With Vercel, Netlify & Cloudflare Configs)

Missing security headers are the easiest vulnerability to fix and the most common to miss. Copy-paste configs for Vercel, Netlify, Cloudflare Pages, and Next.js.

Proveably Team

2026-02-25

Security Headers Every Web App Needs

You deployed your app. It works. Users are signing up. But open your browser's DevTools, check the response headers, and you'll probably see... nothing.

Missing security headers are the most common finding in our scans. They're also the easiest to fix — usually a single config file.

Why Headers Matter

Security headers tell the browser how to behave. Without them:

  • Your app can be embedded in malicious iframes (clickjacking)
  • Scripts from any domain can run on your page (XSS attacks)
  • Browsers will send credentials to any site
  • Users can be downgraded from HTTPS to HTTP

All major vulnerability scanners flag missing headers. And if an enterprise customer ever runs a pentest on your app, this is the first thing they'll find.

The 6 Essential Headers

1. Content-Security-Policy (CSP)

What it does: Controls which sources can load scripts, styles, images, and other resources on your page.

Why it matters: Prevents XSS attacks by blocking inline scripts and unauthorized external scripts.

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

Note for vibe coders: If you use Supabase, add your project URL to connect-src. If you use Google Fonts, add https://fonts.googleapis.com to style-src and https://fonts.gstatic.com to font-src.

2. Strict-Transport-Security (HSTS)

What it does: Forces browsers to always use HTTPS, even if the user types http://.

Why it matters: Prevents SSL stripping attacks and protects users on public WiFi.

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

3. X-Frame-Options

What it does: Prevents your app from being embedded in an iframe.

Why it matters: Stops clickjacking attacks where an attacker overlays an invisible frame of your app on a malicious page.

X-Frame-Options: DENY

4. X-Content-Type-Options

What it does: Prevents MIME-type sniffing.

Why it matters: Stops browsers from interpreting files as a different MIME type, which can lead to XSS via uploaded files.

X-Content-Type-Options: nosniff

5. Referrer-Policy

What it does: Controls how much URL information is shared when navigating away from your site.

Why it matters: Prevents sensitive URL parameters (tokens, user IDs) from leaking to third-party sites.

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

6. Permissions-Policy

What it does: Controls which browser features (camera, microphone, geolocation) your app can use.

Why it matters: Prevents malicious scripts from accessing sensitive device features.

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

Copy-Paste Configs

Vercel (vercel.json)

{
  "headers": [
    {
      "source": "/(.*)",
      "headers": [
        {
          "key": "Content-Security-Policy",
          "value": "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; connect-src 'self' https://*.supabase.co"
        },
        {
          "key": "Strict-Transport-Security",
          "value": "max-age=31536000; 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=()"
        }
      ]
    }
  ]
}

Netlify (_headers)

/*
  Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; connect-src 'self' https://*.supabase.co
  Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
  X-Frame-Options: DENY
  X-Content-Type-Options: nosniff
  Referrer-Policy: strict-origin-when-cross-origin
  Permissions-Policy: camera=(), microphone=(), geolocation=()

Cloudflare Pages (_headers)

Same format as Netlify:

/*
  Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; connect-src 'self' https://*.supabase.co
  Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
  X-Frame-Options: DENY
  X-Content-Type-Options: nosniff
  Referrer-Policy: strict-origin-when-cross-origin
  Permissions-Policy: camera=(), microphone=(), geolocation=()

Next.js (next.config.js)

/** @type {import('next').NextConfig} */
const nextConfig = {
  async headers() {
    return [
      {
        source: '/(.*)',
        headers: [
          {
            key: 'Content-Security-Policy',
            value: "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; connect-src 'self' https://*.supabase.co",
          },
          {
            key: 'Strict-Transport-Security',
            value: 'max-age=31536000; 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=()',
          },
        ],
      },
    ];
  },
};

module.exports = nextConfig;

Testing Your Headers

Quick Test

Open DevTools → Network tab → Click your page request → Check the Response Headers section.

Automated Test

Use Proveably to scan your app — we check all 6 headers and flag any that are missing or misconfigured, with specific fix instructions for your hosting platform.

Common Gotchas

  1. CSP breaks inline styles. Most UI libraries use inline styles. Start with 'unsafe-inline' for style-src and tighten later.
  2. CSP blocks Supabase. Add https://*.supabase.co to connect-src or your API calls will fail silently.
  3. X-Frame-Options blocks your own embeds. If you need to iframe your app somewhere, use SAMEORIGIN instead of DENY.
  4. Vercel/Netlify already set HSTS. They do — but adding your own header ensures consistency and adds preload support.

Check Your Headers Now

Your headers take 5 minutes to fix. Scan your app to see exactly which ones you're missing:

Scan my app free →


Related reading:

Ready to automate your compliance?

Start scanning in minutes. No credit card required.

Get Started Free

Report a Bug

Help us improve by reporting issues

Screenshot
Page:
Browser:
Time:

Bug Report Submitted

Thank you! We'll investigate this issue.