IdeaValue

How to Set Security Headers: X-Frame-Options, nosniff, Referrer-Policy and Permissions-Policy

By the IDEAVALU team · Updated

A few lines of response headers turn on defenses built into the browser. Many indie sites launch without any of them.

What each header does

Each one blocks something different.

  • X-Frame-Options (or CSP frame-ancestors): stops clickjacking, where another site embeds yours in an invisible iframe and tricks visitors into clicking buttons.
  • X-Content-Type-Options: nosniff: stops the browser from guessing a file type and executing, say, an image as a script.
  • Referrer-Policy: decides how much of the URL is sent to linked sites. With strict-origin-when-cross-origin, other sites receive only your domain, so IDs and tokens in your URLs do not leak.
  • Permissions-Policy: turns off browser features you do not use, such as the camera, microphone and location, so embedded third-party scripts cannot use them either.

Check it yourself

This command shows the main headers at once.

Show the main security headers

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

Version disclosure and security.txt

A version such as "nginx/1.18.0" or "PHP/7.4" in the Server or X-Powered-By header helps attackers target known bugs in old releases. Hide it, and update the software itself.

Publishing a contact in /.well-known/security.txt tells anyone who finds a vulnerability where to report it (RFC 9116). At minimum, include the Contact and Expires lines.

Example public/.well-known/security.txt

Contact: mailto:security@example.com
Expires: 2027-12-31T23:59:59.000Z
Preferred-Languages: en

How to fix

Clickjacking protection

Forbid embedding in other sites with X-Frame-Options (or CSP frame-ancestors).

Next.js

// next.config.mjs
const securityHeaders = [
  { key: 'Strict-Transport-Security', value: 'max-age=63072000; includeSubDomains' },
  { 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=()' },
]

export default {
  async headers() {
    return [{ source: '/:path*', headers: securityHeaders }]
  },
}

Static sites, Vite and others on Vercel

// vercel.json (for non-Next.js sites on Vercel)
{
  "headers": [
    {
      "source": "/(.*)",
      "headers": [
        { "key": "Strict-Transport-Security", "value": "max-age=63072000; includeSubDomains" },
        { "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 and Cloudflare Pages

# public/_headers (Netlify, Cloudflare Pages)
/*
  Strict-Transport-Security: max-age=63072000; includeSubDomains
  X-Frame-Options: SAMEORIGIN
  X-Content-Type-Options: nosniff
  Referrer-Policy: strict-origin-when-cross-origin
  Permissions-Policy: camera=(), microphone=(), geolocation=()

One config fixes HSTS, clickjacking, nosniff, Referrer-Policy and Permissions-Policy together. If your app uses the camera or location, change that feature to self in Permissions-Policy.

X-Content-Type-Options

Add X-Content-Type-Options: nosniff. The config is the same as above.

Referrer-Policy

With Referrer-Policy: strict-origin-when-cross-origin, other sites receive only your domain. The config is the same as above.

Permissions-Policy

Disable browser features you do not use with Permissions-Policy. The config is the same as above.

Software version disclosure

Remove version numbers from response headers.

nginx

server_tokens off;

Express

app.disable('x-powered-by')

PHP (php.ini)

expose_php = Off

FAQ

Should I use X-Frame-Options or frame-ancestors?
Modern browsers give CSP frame-ancestors priority. If you do not use a CSP, X-Frame-Options: SAMEORIGIN is enough. Setting both is fine.
How to Set Security Headers like X-Frame-Options and nosniff | IDEAVALU