Every website communicates with browsers through HTTP headers. While most developers focus on the content these headers deliver, a critical subset exists specifically to protect your visitors and your reputation. Security headers instruct browsers to enforce rules that prevent entire classes of attacks, from clickjacking and cross-site scripting (XSS) to protocol downgrade exploits.
Despite their importance, a staggering number of websites ship without basic security headers. According to recent scans, over 60% of the top one million sites still lack a proper Content-Security-Policy, and nearly 40% do not enforce HSTS. The good news: adding these headers is straightforward, and the protection they provide is immediate.
This guide walks you through every security header that matters in 2026, explains what each one does, and gives you copy-paste configuration for both Nginx and Apache.
Why Security Headers Matter
When a browser loads your page, it trusts you to tell it how to behave. Without explicit instructions, the browser falls back to permissive defaults: it may render your site inside a third-party iframe, allow inline scripts from any origin, or silently downgrade from HTTPS to HTTP.
Attackers exploit these permissive defaults daily. A missing X-Frame-Options header enables clickjacking, where your login page is embedded invisibly inside an attacker's site. A missing Content-Security-Policy allows injected scripts to steal session cookies. A missing Strict-Transport-Security header means a man-in-the-middle can strip your HTTPS and intercept credentials in plaintext.
Security headers close these gaps with a single line of server configuration. They cost nothing to implement and dramatically reduce your attack surface.
Strict-Transport-Security (HSTS)
HSTS tells the browser to only communicate with your server over HTTPS, even if the user types http:// in the address bar or clicks an HTTP link.
How It Works
When your server sends the Strict-Transport-Security header, the browser stores a directive that forces all future requests to your domain over HTTPS for a specified duration. This eliminates the initial insecure HTTP request that attackers intercept during SSL-stripping attacks.
Recommended Configuration
Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
- max-age=63072000: Enforce HSTS for 2 years (in seconds).
- includeSubDomains: Apply to all subdomains, preventing attacks on
api.example.comorcdn.example.com. - preload: Allows your domain to be included in browser preload lists, so HSTS is enforced before the first visit.
Important Notes
Only enable includeSubDomains if every subdomain supports HTTPS. Start with a shorter max-age (e.g., 300 seconds) during testing, then increase once you confirm everything works.
Content-Security-Policy (CSP)
CSP is the most powerful — and most complex — security header. It defines exactly which sources of content the browser may load for each resource type: scripts, styles, images, fonts, frames, and more.
Why CSP Is Essential
Cross-site scripting (XSS) remains one of the most common web vulnerabilities. CSP neutralizes most XSS attacks by preventing the browser from executing inline scripts or loading scripts from unauthorized origins.
Common Directives
Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.example.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' https://fonts.gstatic.com; frame-ancestors 'none'; base-uri 'self'; form-action 'self'
- default-src 'self': Only allow resources from your own origin by default.
- script-src: Whitelist specific script origins. Avoid
'unsafe-inline'and'unsafe-eval'for scripts whenever possible. - style-src: Allow inline styles if your framework requires it, but prefer external stylesheets.
- img-src: Allow data URIs and HTTPS sources for images.
- frame-ancestors 'none': Prevent your site from being embedded in iframes (replaces X-Frame-Options).
- base-uri 'self': Prevent
<base>tag injection. - form-action 'self': Prevent form submissions to external domains.
Starting with Report-Only
If you are unsure what your site loads, start with Content-Security-Policy-Report-Only instead. This reports violations without blocking them, letting you fine-tune the policy before enforcing it.
Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-report
X-Frame-Options
This header controls whether your site can be embedded inside an <iframe> on another domain. This prevents clickjacking attacks where an attacker overlays invisible elements on top of your page.
Recommended Configuration
X-Frame-Options: DENY
- DENY: Never allow framing, from any origin.
- SAMEORIGIN: Allow framing only from your own domain (useful if you embed your own pages).
Note that frame-ancestors in CSP provides more granular control and is the modern replacement, but X-Frame-Options is still recommended for backward compatibility with older browsers.
X-Content-Type-Options
Browsers sometimes try to "sniff" the content type of a response, ignoring the declared Content-Type header. This can lead to security issues if a browser interprets a text file as HTML or JavaScript.
Recommended Configuration
X-Content-Type-Options: nosniff
This single directive tells the browser to strictly follow the declared MIME type. Always set it.
Referrer-Policy
This header controls how much referrer information is sent when your users click links or load resources from external origins.
Recommended Configuration
Referrer-Policy: strict-origin-when-cross-origin
This sends only the origin (not the full URL path) when navigating to a different domain, and sends the full referrer for same-origin requests. It balances analytics needs with user privacy.
Other useful values include no-referrer (send nothing) and same-origin (only send referrer for same-origin requests).
Permissions-Policy
Formerly known as Feature-Policy, this header lets you control which browser features your site can use: camera, microphone, geolocation, payment, and more.
Recommended Configuration
Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=(), usb=(), magnetometer=(), gyroscope=(), accelerometer=()
The empty parentheses () disable each feature entirely. This is ideal for most websites that do not need access to hardware sensors or payment APIs.
If you do need a feature (e.g., geolocation for a mapping page), you can scope it:
Permissions-Policy: geolocation=(self "https://maps.example.com")
How to Test Your Security Headers
You can manually inspect headers using browser DevTools (Network tab), but automated tools give you a comprehensive and repeatable assessment.
WMSS runs over 80 automated checks on your site, including a detailed security headers audit. It verifies each header described in this guide, flags missing or misconfigured directives, and provides specific remediation steps tailored to your stack. Run a free scan at wmss.me to see where your headers stand.
Other useful tools include Mozilla Observatory and SecurityHeaders.com.
Nginx Configuration Example
Add these headers to your server block or a shared include file:
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self'; frame-ancestors 'none'; base-uri 'self'; form-action 'self'" 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=()" always;
The always parameter ensures headers are sent even for error responses (4xx, 5xx).
Apache Configuration Example
Add these directives to your .htaccess or virtual host configuration:
Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
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 'none'; base-uri 'self'; form-action 'self'"
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=()"
Make sure mod_headers is enabled: a2enmod headers && systemctl restart apache2.
Conclusion
Security headers are the lowest-effort, highest-impact improvement you can make to your website's security posture. Each header described in this guide addresses a specific class of attack, and together they form a robust defense layer that costs nothing but a few lines of server configuration.
Start with HSTS and X-Content-Type-Options (the simplest to deploy), then work your way up to a strict Content-Security-Policy. Use WMSS to audit your headers instantly and track your progress over time. Your visitors trust you with their browsers — make sure that trust is well placed.