1dollar.website

Securing a site on Vercel

Vercel handles HTTPS for you, but security headers are your job: add them in vercel.json (or next.config.js for Next.js). The most serious Vercel problem we see is a secret key exposed in browser code through a NEXT_PUBLIC_ variable.

Last updated October 26, 2026

Prefer to fill in a form? Use the free security headers generator or the SPF and DMARC record generator.

Not sure whether your site has this?

Paste your address. The checkup is free and shows what's flagged.

What our checkup looks for

Our free checkup reads your site's headers, checks the HTTPS redirect, asks for files that should never be public, and checks your domain's email records. It recognises Vercel and Next.js from their response headers.

Every security header in vercel.json

Open (or create) vercel.json in your project root and add this block. It sets every recommended header on every page. The Content Security Policy starts in report-only mode, so it changes nothing for visitors yet.

On Next.js you can set the same headers in next.config.js with an async headers() function instead. Use one place, not both. Next.js also sends X-Powered-By: Next.js; set poweredByHeader: false to stop it.

How to fix it on Vercel

  1. Open (or create) vercel.json in the root of your project.
  2. Add the headers block below, commit, and redeploy.
  3. The Content-Security-Policy-Report-Only line changes nothing for visitors yet. After a week with no broken features (check your browser's console), rename it to Content-Security-Policy to switch it on.
json
{
  "headers": [
    {
      "source": "/(.*)",
      "headers": [
        {
          "key": "Strict-Transport-Security",
          "value": "max-age=31536000; includeSubDomains"
        },
        {
          "key": "Content-Security-Policy-Report-Only",
          "value": "default-src 'self' https:; img-src 'self' https: data:; object-src 'none'; base-uri 'self'; frame-ancestors 'self'; upgrade-insecure-requests"
        },
        {
          "key": "X-Frame-Options",
          "value": "SAMEORIGIN"
        },
        {
          "key": "X-Content-Type-Options",
          "value": "nosniff"
        },
        {
          "key": "Referrer-Policy",
          "value": "strict-origin-when-cross-origin"
        },
        {
          "key": "Permissions-Policy",
          "value": "camera=(), microphone=(), geolocation=()"
        }
      ]
    }
  ]
}

HTTPS

Vercel serves HTTPS and redirects automatically for domains added in Project > Settings > Domains. If the checkup flags HTTPS, the domain is usually missing there or its DNS points elsewhere.

How to fix it on Vercel

  1. Vercel serves HTTPS and redirects automatically for domains added in Project > Settings > Domains.
  2. If this domain isn't listed there, add it. If it points somewhere else, update its DNS records to Vercel's.

Never put a secret key in NEXT_PUBLIC_

Next.js copies every environment variable starting with NEXT_PUBLIC_ into the JavaScript sent to visitors' browsers, where anyone can read it. That's fine for publishable keys, and a disaster for a Stripe secret key, a database password or an AI API key. Keep those unprefixed and use them only in API routes or server code.

How to fix it on Vercel

  1. Treat the key as stolen: create a new one in that service's dashboard and delete the old one today.
  2. Move every call that needs the secret key to your server (an API route or serverless function). Browser code should only ever hold publishable keys.
  3. Rebuild and redeploy, then search the new JavaScript files to confirm the key is gone.

Files deployed by accident

Files in public/ are served as-is, so a stray .env or backup there goes live. Source maps let anyone rebuild your original code; Next.js keeps them off in production unless productionBrowserSourceMaps is on.

Choose where your site runs

Vercel
  1. Treat every password and key in that file as stolen. Change them all now: database password, API keys, payment keys, email passwords.
  2. This file is being deployed as part of your site. Remove it from the folder you publish (e.g. public/ or the build output).
  3. Check your build settings so the project root isn't deployed as-is, then redeploy.
  4. Afterwards, open the address again in a private window and confirm it shows an error page.
Copy this
# Make sure files like these never reach your publish folder
.env
.env.*
.git/
Source maps
  1. Turn off public source maps in your build: Create React App: GENERATE_SOURCEMAP=false. Vite: build.sourcemap = false. Next.js: leave productionBrowserSourceMaps off. Webpack: devtool: false (or 'hidden-source-map').
  2. Delete the existing .map files from your server, or block them as shown below.
  3. If you need them for error tracking, upload them to your error tool privately instead of serving them.
nginx
location ~* \.map$ {
    return 404;
}

Your domain's email records

If your domain's DNS is on Vercel, add these under the domain's DNS records. Otherwise add them wherever your DNS lives. Our email guide explains each one.

How to fix it

  1. Log in to wherever you manage your domain's DNS (your registrar, Cloudflare, etc.).
  2. Add both records below to example.com. If there's already a TXT record starting with v=spf1, replace it: a domain must have only one.
  3. Replace the <...> part with your email provider's SPF include: Google Workspace is _spf.google.com, Microsoft 365 is spf.protection.outlook.com. Add one for each service that sends email as you.
  4. If you're not sure every service that sends email as you is listed, start with p=none for two weeks and read the reports, then change it to p=quarantine.
dns
Type: TXT
Name: @
Value: v=spf1 include:<your email provider's SPF domain> -all

Type: TXT
Name: _dmarc
Value: v=DMARC1; p=quarantine; rua=mailto:dmarc@example.com

How to check it worked

Each header should appear after your next deploy. Or run our free checkup again.

bash
curl -sI https://example.com | grep -iE 'strict-transport|x-frame|x-content-type|referrer-policy|permissions-policy|content-security'

Common mistakes

  • Setting headers in both vercel.json and next.config.js with different values.
  • A secret key in a NEXT_PUBLIC_ variable. Rotate it and move the call to an API route.
  • Adding headers to a preview deployment and not checking production.

What this can't tell you

We check from outside, so we can't see your API routes or environment variables. A code review can.

Check your own site, free

The free checkup shows how many results are flagged and how serious. For $1, the report picks the ones that matter for your site and writes each fix for your platform, with your own domain filled in.