Proper HTTP header configuration is the first line of defense against many website attacks. They protect users from XSS, clickjacking, man-in-the-middle, and other threats. This guide covers all key security headers with configurations for various servers.

Why Are Security Headers Important?

  • Protect against XSS, clickjacking, MIME sniffing attacks
  • Enforce HTTPS connections
  • Control access to browser features
  • Are checked by security audits
  • Affect scores in tools like SecurityHeaders.com

1. Strict-Transport-Security (HSTS)

HSTS enforces HTTPS connections for all requests, eliminating man-in-the-middle attack possibilities.

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

Parameters

ParameterMeaning
max-ageValidity time in seconds (31536000 = 1 year)
includeSubDomainsApply to all subdomains
preloadAllow addition to browser preload lists

HSTS Preload List

Adding to the HSTS Preload List means browsers will enforce HTTPS even on first visit. Requirements:

  • Valid SSL certificate
  • HTTP → HTTPS redirect
  • HSTS with max-age minimum 1 year
  • includeSubDomains and preload directives

Note: HSTS is irreversible for the max-age duration. Start with a short period (e.g., 300 seconds) and gradually increase.

2. Content-Security-Policy (CSP)

CSP defines allowed sources for scripts, styles, images, and other resources. It’s the most important XSS protection header.

Content-Security-Policy: default-src 'self'; script-src 'self' https://trusted.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; frame-ancestors 'self'

Main Directives

DirectiveControls
default-srcDefault source for all types
script-srcJavaScript scripts
style-srcCSS styles
img-srcImages
font-srcFonts
connect-srcXHR, fetch, WebSocket
frame-srciframe sources
frame-ancestorsWho can embed page in iframe

Report-Only Mode

Test CSP without blocking:

Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-report

Detailed guide: CSP for Google Services

3. X-Frame-Options

X-Frame-Options protects against clickjacking attacks by controlling whether a page can be embedded in an iframe.

X-Frame-Options: SAMEORIGIN

Values

ValueMeaning
DENYNever allow iframe embedding
SAMEORIGINOnly from same domain
ALLOW-FROM uriOnly from specified source (deprecated)

Note: CSP frame-ancestors is the newer alternative. Use both for backward compatibility.

4. X-Content-Type-Options

X-Content-Type-Options prevents “MIME sniffing” - browser attempts to guess file type.

X-Content-Type-Options: nosniff

Without this header, the browser may interpret a text file as JavaScript, enabling XSS attacks.

5. Referrer-Policy

Referrer-Policy controls what source information is sent during navigation.

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

Values

ValueMeaning
no-referrerNever send referrer
same-originOnly for same domain
strict-originDomain only, not on HTTPS→HTTP downgrade
strict-origin-when-cross-originFull URL for same-origin, domain for cross-origin (recommended)

6. Permissions-Policy

Permissions-Policy (formerly Feature-Policy) controls access to browser features like geolocation, camera, or microphone.

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

Syntax

  • () - blocked for all
  • (self) - only this domain
  • * - allowed for all
FeatureControls
geolocationGeolocation API
microphoneMicrophone access
cameraCamera access
paymentPayment Request API
fullscreenFullscreen API

7. X-XSS-Protection (Deprecated)

X-XSS-Protection activated the built-in XSS filter in older browsers. It is deprecated - modern browsers (Chrome 78+) removed this filter.

X-XSS-Protection: 0

Why 0? The value 1; mode=block can cause side-channel attacks. Use CSP instead.

Complete Configuration

Apache (.htaccess)

<IfModule mod_headers.c>
    # HSTS - enforce HTTPS
    Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"

    # CSP - customize to your needs
    Header always set Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self'; frame-ancestors 'self'"

    # Clickjacking protection
    Header always set X-Frame-Options "SAMEORIGIN"

    # Prevent MIME sniffing
    Header always set X-Content-Type-Options "nosniff"

    # Referrer control
    Header always set Referrer-Policy "strict-origin-when-cross-origin"

    # Browser API restrictions
    Header always set Permissions-Policy "geolocation=(), microphone=(), camera=()"

    # Disable deprecated XSS filter
    Header always set X-XSS-Protection "0"
</IfModule>

Nginx

server {
    # HSTS
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;

    # CSP
    add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; frame-ancestors 'self'" always;

    # Clickjacking
    add_header X-Frame-Options "SAMEORIGIN" always;

    # MIME sniffing
    add_header X-Content-Type-Options "nosniff" always;

    # Referrer
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;

    # Permissions
    add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;

    # XSS filter disabled
    add_header X-XSS-Protection "0" always;
}

Node.js (Express + Helmet)

const helmet = require('helmet');

app.use(helmet({
  contentSecurityPolicy: {
    directives: {
      defaultSrc: ["'self'"],
      scriptSrc: ["'self'"],
      styleSrc: ["'self'", "'unsafe-inline'"],
      imgSrc: ["'self'", "data:", "https:"],
      frameAncestors: ["'self'"]
    }
  },
  hsts: {
    maxAge: 31536000,
    includeSubDomains: true,
    preload: true
  },
  frameguard: { action: 'sameorigin' },
  noSniff: true,
  referrerPolicy: { policy: 'strict-origin-when-cross-origin' },
  xssFilter: false
}));

Netlify (netlify.toml)

[[headers]]
  for = "/*"
  [headers.values]
    Strict-Transport-Security = "max-age=31536000; includeSubDomains; preload"
    X-Frame-Options = "SAMEORIGIN"
    X-Content-Type-Options = "nosniff"
    Referrer-Policy = "strict-origin-when-cross-origin"
    Permissions-Policy = "geolocation=(), microphone=(), camera=()"

Vercel (vercel.json)

{
  "headers": [
    {
      "source": "/(.*)",
      "headers": [
        { "key": "Strict-Transport-Security", "value": "max-age=31536000; includeSubDomains; preload" },
        { "key": "X-Frame-Options", "value": "SAMEORIGIN" },
        { "key": "X-Content-Type-Options", "value": "nosniff" },
        { "key": "Referrer-Policy", "value": "strict-origin-when-cross-origin" }
      ]
    }
  ]
}

Testing Headers

SecurityHeaders.com

https://securityheaders.com/ - quick scan with A-F grade.

Observatory by Mozilla

https://observatory.mozilla.org/ - comprehensive security audit.

Chrome DevTools

  1. Open DevTools (F12)
  2. Network tab
  3. Select HTML document
  4. Check Response Headers

curl

curl -I https://example.com

Security Checklist

Critical

  • HSTS - enforce HTTPS
  • X-Frame-Options - clickjacking protection
  • X-Content-Type-Options: nosniff
  • CSP - XSS protection
  • Referrer-Policy - source information control
  • Permissions-Policy - browser API restrictions

Deprecated (disable)

  • X-XSS-Protection: 0 - disable legacy filter

Summary

Security header configuration is a simple step that significantly increases site protection:

  1. HSTS enforces HTTPS and protects against downgrade attacks
  2. CSP is the most powerful XSS protection
  3. X-Frame-Options blocks clickjacking
  4. Test regularly on SecurityHeaders.com and Observatory

Well-configured headers can prevent many attacks with minimal effort.

Sources

  1. MDN - HTTP Security Headers https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers#security

  2. OWASP - Security Headers https://owasp.org/www-project-secure-headers/

  3. SecurityHeaders.com https://securityheaders.com/

  4. Mozilla Observatory https://observatory.mozilla.org/

  5. HSTS Preload List https://hstspreload.org/