Somebody on the team ran your site through securityheaders.com or Mozilla Observatory, and it came back with an F. Nothing on the site is actually broken — pages load, forms submit, checkout works — but the report is full of red X's next to headers you've never heard of. This is a genuinely easy fix, and it's worth doing whether you're on shared cPanel hosting or running your own Nginx VPS.
What These Headers Actually Do
HTTP response headers are extra instructions your server sends alongside every page, invisible to visitors but read by the browser. Security headers tell the browser to enforce restrictions on its own — block your site from being embedded in someone else's iframe, refuse to guess a file's type, strip referrer data leaking to third parties. None of this is enabled by default on a stock Apache or Nginx install, which is exactly why a fresh cPanel account or a freshly provisioned VPS scores an F out of the box.
Symptom: F Grade, No Obvious Bug
The usual trigger is a client, an auditor, or a security-conscious visitor running your domain through a headers scanner. The report lists several missing headers and the site "loses points" even though functionally nothing is wrong. Sometimes this surfaces during a PCI compliance check for a WooCommerce store, or a vendor security questionnaire before a B2B deal closes.
Cause: Your Web Server Doesn't Send Them Unless Told To
Apache (used under most cPanel hosting) and Nginx (common on VPS setups) ship with sane defaults for serving files, not for hardening the browser side of things. Security headers have to be added explicitly, either in your .htaccess file, your virtual host config, your application code, or at a layer in front of your server like Cloudflare. If you migrated hosts recently, this is also a common regression — headers configured on the old server rarely get carried over automatically.
The Headers Worth Adding
You don't need all of these on day one. Start with the first four — they're low-risk and won't break your site. Content-Security-Policy is the one that needs care, covered separately below.
| Header | What It Does | Safe Starting Value |
|---|---|---|
| X-Frame-Options | Stops your pages being loaded inside an iframe on another site (clickjacking protection) | SAMEORIGIN |
| X-Content-Type-Options | Stops the browser guessing a file's type from its content instead of trusting the declared type | nosniff |
| Referrer-Policy | Controls how much of your URL gets sent as the referrer when a user clicks an outbound link | strict-origin-when-cross-origin |
| Permissions-Policy | Disables browser features (camera, microphone, geolocation) your site doesn't use | camera=(), microphone=(), geolocation=() |
| Content-Security-Policy | Restricts which sources scripts, styles and images can load from | Start in report-only mode (see below) |
Fix: Adding Headers in cPanel (Apache/.htaccess)
Most cPanel accounts have mod_headers enabled already. Edit (or create) .htaccess in your site's document root via File Manager, or over SFTP, and add:
<IfModule mod_headers.c>
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=()"
</IfModule>
Save, then reload the site and check your response headers with curl -I https://yourdomain.com. If you don't see them, check that mod_headers isn't disabled in WHM's Apache module list — it's rare, but some minimal configurations turn it off.
Fix: Adding Headers on an Nginx VPS
Add these inside your server { } block, then test and reload:
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;
sudo nginx -t && sudo systemctl reload nginx
If you're running Nginx as a reverse proxy in front of PHP-FPM or a Node app, set these at the server block level rather than inside a location block, so they apply to every response including static assets.
If You're Behind Cloudflare
You can add these headers at the edge instead of touching server config: Rules > Transform Rules > Modify Response Header in the Cloudflare dashboard, one rule per header, applied to all incoming requests. This is useful if you don't have shell access, or if you want the headers to apply consistently across multiple origin servers behind the same Cloudflare zone.
A Word on Content-Security-Policy
CSP is the header that actually breaks sites when done carelessly, because it can block your own inline scripts, Google Fonts, analytics tags, or a payment gateway's embedded widget. Don't copy a strict CSP from a blog post and paste it live. Instead, ship it in report-only mode first:
Header always set Content-Security-Policy-Report-Only "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data:;"
Report-only mode logs violations in the browser console without blocking anything. Browse your site for a few days, note what gets flagged (a font CDN, a chat widget domain, an ad script), add those sources to the policy, and only switch Content-Security-Policy-Report-Only to Content-Security-Policy once nothing legitimate is being blocked.
Prevention
- Keep a copy of your header config outside the server — in your repo, a README, or a WHM backup note — so a migration or server rebuild doesn't silently drop it.
- Re-scan with securityheaders.com after any hosting migration, Nginx config rewrite, or Cloudflare zone change; these are the three events most likely to reset headers to defaults.
- Never enable enforced CSP without running it in report-only mode first, even on a site you think is simple — third-party embeds (chat widgets, payment forms, fonts) are the usual surprise.
- If multiple subdomains share a codebase, apply headers at the vhost/server-block level rather than per-page, so a new page added later doesn't ship unprotected by accident.
Frequently Asked Questions
Will adding these headers slow my site down?
No. They're a few extra bytes per response and cost nothing in server processing time. The only header that can cause a real slowdown is a badly configured CSP that forces you to inline resources you'd normally load from a fast CDN — and that's a configuration choice, not a property of the header itself.
Do I need HSTS too?
HSTS (forcing HTTPS) is a separate, more sensitive header with its own risks if misconfigured — we've covered it in detail in a dedicated guide. Add the headers here first, confirm your site is stable, then consider HSTS on its own once you're comfortable everything else is working.
My site uses WordPress — can I add these with a plugin instead?
Yes, plugins like Really Simple SSL or a dedicated security header plugin can set these without touching server files, which is fine for most sites. The tradeoff is you're relying on WordPress loading correctly to serve the headers at all — a server-level fix in .htaccess or Nginx config applies even if WordPress itself is erroring out.
I added the headers but the scanner still shows them missing. Why?
Usually a caching layer — Cloudflare, a page cache plugin, or Varnish — is serving a cached response captured before you made the change. Purge your CDN and page cache, then re-test with curl -I directly against your domain to confirm the header is actually present before blaming the scanner.
Can these headers break my payment gateway or embedded checkout widget?
X-Frame-Options can, if your checkout is meant to be embedded in an iframe on another domain — SAMEORIGIN blocks that. Content-Security-Policy is the more common culprit, since gateways often load scripts or open iframes from their own domain. Check your payment provider's documentation for the exact domains to whitelist before enforcing a strict CSP on a store.
