Guides

Essential HTTP Security Headers: A Complete Implementation Guide

March 20, 2026 · 10 min read

Table of Contents

HTTP security headers are one of the most effective and underutilized defenses available to web application operators. These response headers instruct browsers to enable built-in security features that protect users against cross-site scripting (XSS), clickjacking, protocol downgrade attacks, and a range of other client-side threats.

Despite their effectiveness, adoption remains inconsistent. An analysis of the top 1 million websites found that only 12.8% implement a meaningful Content-Security-Policy, and just 25.3% use HSTS. This guide covers the essential headers every site should implement, with practical configuration examples for the most common server environments.

Why Security Headers Matter

Security headers operate at the browser level, providing a final layer of defense even when application code contains vulnerabilities. A well-configured Content-Security-Policy can prevent the exploitation of an XSS vulnerability that might otherwise lead to account takeover. HSTS ensures that users connect securely even if an attacker attempts to intercept the initial connection.

These headers are free to implement, require no software installation, and can be deployed at the web server, reverse proxy, or CDN level. There is no reason for any production website to lack basic security header coverage.

Content-Security-Policy (CSP)

CSP is the most powerful — and most complex — security header available. It defines a whitelist of approved content sources for each resource type (scripts, styles, images, fonts, etc.), preventing the browser from loading unauthorized content.

What CSP Prevents: Cross-site scripting (XSS), data injection attacks, clickjacking (via frame-ancestors), mixed content loading, and unauthorized resource loading from third-party domains.

Recommended Starting Policy

Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self'; connect-src 'self'; frame-ancestors 'self'; base-uri 'self'; form-action 'self';

This baseline policy restricts all resources to the same origin by default, allows inline styles (often necessary for legacy applications), permits images from HTTPS sources, and prevents the site from being framed by other domains.

Apache Configuration

# In .htaccess or httpd.conf
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'; connect-src 'self'; frame-ancestors 'self'; base-uri 'self'; form-action 'self';"

Nginx Configuration

# In server block or location block
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self'; connect-src 'self'; frame-ancestors 'self'; base-uri 'self'; form-action 'self';" always;

Cloudflare Configuration

In Cloudflare, security headers can be configured through Transform Rules (HTTP Response Header Modification) in the dashboard under Rules, or via Cloudflare Workers for more complex logic:

// Cloudflare Worker example
addEventListener('fetch', event => {
  event.respondWith(handleRequest(event.request));
});

async function handleRequest(request) {
  const response = await fetch(request);
  const newResponse = new Response(response.body, response);
  newResponse.headers.set('Content-Security-Policy',
    "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self'; connect-src 'self'; frame-ancestors 'self'; base-uri 'self'; form-action 'self';");
  return newResponse;
}

Pro tip: Start with Content-Security-Policy-Report-Only instead of Content-Security-Policy to monitor violations without blocking content. Add a report-uri or report-to directive to collect violation reports, then tighten the policy iteratively until no legitimate resources are blocked.

Strict-Transport-Security (HSTS)

HSTS instructs browsers to only connect to your site via HTTPS, preventing protocol downgrade attacks and cookie hijacking. Once a browser receives an HSTS header, it will automatically convert all HTTP requests to HTTPS for the specified duration — even if the user types http:// explicitly.

Recommended Configuration

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

Apache

Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"

Nginx

add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
Important: Before enabling includeSubDomains, verify that all subdomains support HTTPS. If any subdomain is HTTP-only, users will be unable to access it. Before submitting to the preload list at hstspreload.org, understand that removal is difficult and slow — only preload when you are fully committed to HTTPS across your entire domain.

X-Frame-Options

X-Frame-Options prevents your site from being embedded in iframes on other domains, defending against clickjacking attacks where an attacker overlays invisible frames to trick users into clicking on your site's controls.

X-Frame-Options: DENY

Use DENY to prevent all framing, or SAMEORIGIN to allow framing only from pages on the same origin. Note that CSP's frame-ancestors directive is the modern replacement and offers more granular control, but X-Frame-Options should still be set for compatibility with older browsers.

Apache

Header always set X-Frame-Options "DENY"

Nginx

add_header X-Frame-Options "DENY" always;

X-Content-Type-Options

This header prevents browsers from MIME-sniffing a response away from the declared content type. Without it, browsers may interpret a file as a different type than what the server specified — for example, treating a text file as JavaScript — which can enable XSS attacks through uploaded files.

X-Content-Type-Options: nosniff

This is a simple, one-value header with no configuration options. There is no reason not to set it on every response.

Apache

Header always set X-Content-Type-Options "nosniff"

Nginx

add_header X-Content-Type-Options "nosniff" always;

Referrer-Policy

Referrer-Policy controls how much referrer information is sent when users navigate away from your site. By default, browsers may include the full URL — including query parameters that might contain sensitive data like session tokens, search queries, or tracking identifiers.

Recommended Configuration

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

This policy sends the full referrer for same-origin requests (useful for analytics), sends only the origin (domain) for cross-origin HTTPS-to-HTTPS requests, and sends nothing for HTTPS-to-HTTP downgrades. This strikes a good balance between privacy and functionality.

Other useful values:

Apache

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

Nginx

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

Permissions-Policy

Permissions-Policy (formerly Feature-Policy) controls which browser features and APIs can be used on your site. This prevents malicious scripts — whether from XSS or compromised third-party resources — from accessing sensitive capabilities like the camera, microphone, or geolocation.

Recommended Configuration

Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=(), usb=(), magnetometer=(), gyroscope=(), accelerometer=()

The empty parentheses () disable the feature entirely. To allow a feature for your own origin only, use (self). To allow specific third-party origins, list them explicitly: (self "https://trusted-partner.com").

Apache

Header always set Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=(), usb=(), magnetometer=(), gyroscope=(), accelerometer=()"

Nginx

add_header Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=(), usb=(), magnetometer=(), gyroscope=(), accelerometer=()" always;

Common Mistakes

Even when organizations implement security headers, common configuration errors can reduce or eliminate their effectiveness:

Testing Tools

Verify your security header implementation using these tools:

Complete Header Block for Apache:
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'; connect-src 'self'; frame-ancestors 'self'; base-uri 'self'; form-action 'self';"
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
Header always set X-Frame-Options "DENY"
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=(), payment=(), usb=()"
Complete Header Block for Nginx:
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self'; connect-src 'self'; frame-ancestors 'self'; base-uri 'self'; form-action 'self';" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
add_header X-Frame-Options "DENY" 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=(), payment=(), usb=()" always;

Security headers are not a substitute for secure application code, but they provide a critical additional layer of defense that can prevent the exploitation of vulnerabilities that slip through code review and testing. Implementing these headers is one of the highest-impact, lowest-effort security improvements any web application can make.

Powered by ZeroBot

Protect your website from bots, scrapers, and automated threats.

Try ZeroBot Free