Help

Setting headers by platform

Templates for Vercel, Netlify, Next.js, Cloudflare, nginx and Apache, plus what works on WordPress, Shopify, Lovable and Bolt.

All templates set the same six headers with values that almost never break anything. What each one does and how to tighten it later is described in Browser security headers. Here are the values as a list:

Headers
Content-Security-Policy: object-src 'none'; base-uri 'self'; frame-ancestors 'self'; upgrade-insecure-requests
Strict-Transport-Security: max-age=31536000
X-Frame-Options: SAMEORIGIN
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), microphone=(), geolocation=()

Redeploy your site after every change and then check it again. Find your platform below.

Vercel

Create a file named vercel.json in your project’s root folder. If it already exists, add the headers section.

vercel.json
{
  "headers": [
    {
      "source": "/(.*)",
      "headers": [
        {
          "key": "Content-Security-Policy",
          "value": "object-src 'none'; base-uri 'self'; frame-ancestors 'self'; upgrade-insecure-requests"
        },
        {
          "key": "Strict-Transport-Security",
          "value": "max-age=31536000"
        },
        {
          "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=()"
        }
      ]
    }
  ]
}

Netlify

Netlify reads a file named _headers in the folder that gets published. In Vite projects, like the ones Lovable and Bolt generate, it goes into the public folder.

public/_headers
/*
  Content-Security-Policy: object-src 'none'; base-uri 'self'; frame-ancestors 'self'; upgrade-insecure-requests
  Strict-Transport-Security: max-age=31536000
  X-Frame-Options: SAMEORIGIN
  X-Content-Type-Options: nosniff
  Referrer-Policy: strict-origin-when-cross-origin
  Permissions-Policy: camera=(), microphone=(), geolocation=()

Next.js

In next.config you define the headers once for all pages. This works when Next.js runs as a server, for example on Vercel. It has no effect with a static export using output: "export". In that case, use your host’s file from above.

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

const securityHeaders = [
  { key: "Content-Security-Policy", value: "object-src 'none'; base-uri 'self'; frame-ancestors 'self'; upgrade-insecure-requests" },
  { key: "Strict-Transport-Security", value: "max-age=31536000" },
  { 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=()" },
];

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

export default nextConfig;

Cloudflare

If your site runs through Cloudflare, you can add the headers there without touching the server.

  1. In the Cloudflare dashboard, open your domain and go to Rules.
  2. Create a new rule that modifies response headers. It is called a Response Header Transform Rule.
  3. As the condition, choose all incoming requests.
  4. Add one line per header: set, static value, name and value from the list above.

For HSTS, Cloudflare has its own switch in the SSL/TLS settings. Menu names change from time to time, but the terms stay the same.

nginx

nginx
# in your domain's server block
add_header Content-Security-Policy "object-src 'none'; base-uri 'self'; frame-ancestors 'self'; upgrade-insecure-requests" always;
add_header Strict-Transport-Security "max-age=31536000" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
server_tokens off;

Then test with sudo nginx -t and reload with sudo systemctl reload nginx. Note: if a location block has its own add_header, the lines from the server block no longer apply there. In that case, repeat them in the location block.

Apache

Add the lines to the .htaccess file in your site’s root folder. This requires the mod_headers module, which is active with most hosts.

.htaccess
<IfModule mod_headers.c>
  Header always set Content-Security-Policy "object-src 'none'; base-uri 'self'; frame-ancestors 'self'; upgrade-insecure-requests"
  Header always set Strict-Transport-Security "max-age=31536000"
  Header always set X-Frame-Options "SAMEORIGIN"
  Header always set X-Content-Type-Options "nosniff"
  Header always set Referrer-Policy "strict-origin-when-cross-origin"
  Header always set Permissions-Policy "camera=(), microphone=(), geolocation=()"
  Header unset X-Powered-By
</IfModule>

WordPress

You have two options:

  • With a plugin. Search the plugin directory for “security headers”. These plugins set the headers through a settings page, without you having to open any files.
  • At the server level. If your host runs Apache, use the lines from the Apache section. Put them in .htaccess above # BEGIN WordPress. WordPress overwrites the part between BEGIN and END itself.

Managed WordPress hosts often have a dedicated setting for this in their control panel.

Shopify

Your store runs on Shopify’s servers. You can’t add your own headers there, neither in the theme nor in the settings. Shopify sets some security headers itself. Whatever is missing costs points you can’t win back. A CSP as a meta tag in the theme works in the browser against injected scripts and counts as a CSP for us if it sets script-src or default-src. frame-ancestors has no effect in a meta tag, so the protection against embedding stays missing.

On Shopify, focus on what you control. Remove apps and included scripts you no longer need, and set up protection against spoofed email for your domain.

Lovable, Bolt and other app builders

Whether you can set headers depends on where the app runs. On the builder’s own hosting it often isn’t possible, or only to a limited extent. If you connect the project to GitHub and publish it through Netlify or Vercel, use the matching template from above. For apps built with v0, the Vercel and Next.js templates apply.

You can also ask the builder’s assistant to create the file:

Prompt for the assistant
Create a new file public/_headers so that Netlify sets these headers for all pages:

Content-Security-Policy: object-src 'none'; base-uri 'self'; frame-ancestors 'self'; upgrade-insecure-requests
Strict-Transport-Security: max-age=31536000
X-Frame-Options: SAMEORIGIN
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), microphone=(), geolocation=()

Still have questions? Email us.

Is your app
watertight?

Hullwise finds the leaks before someone else does: exposed keys, database credentials and outdated software. Every finding comes with steps to fix it.

https://

Click a finding to mark it as fixed.

See pricingHow a scan works

Ship fast. Stay hullwise.