HTTP security headers are instructions a server sends with every response that restrict how the browser handles the page. HSTS locks the connection to HTTPS, Content-Security-Policy decides where scripts may load from, and X-Content-Type-Options, Referrer-Policy and Permissions-Policy limit type sniffing, referrer leakage and device permissions. You add them in server config, .htaccess or Cloudflare rules.
Why do websites need HTTP security headers?
HTTP security headers are a second layer of defence that makes an attack much harder to exploit in the browser even when the code has a flaw. Cross-site scripting (XSS), clickjacking, HTTPS downgrade and unnecessary data leakage can all be reduced noticeably with a few lines of server-side configuration.
These headers do not replace an SSL certificate or regular updates; they complement them. The certificate encrypts the connection, updates close known holes, and security headers tell the browser what your page does and does not allow. The main ones are:
- —Strict-Transport-Security (HSTS): forces the browser to connect to the domain over HTTPS only.
- —Content-Security-Policy (CSP): allow-lists sources for scripts, styles, images and connections.
- —X-Content-Type-Options: with nosniff, stops the browser guessing file types.
- —Referrer-Policy: controls how much of the URL is shared when users follow links to other sites.
- —Permissions-Policy: switches off browser features such as camera, microphone and geolocation.
- —frame-ancestors / X-Frame-Options: prevents your page from being framed by other sites.
How do you add HSTS, and what should you watch before preloading?
HSTS is added with the Strict-Transport-Security header and tells the browser to use HTTPS only for the domain for a set period. The safe path is to start with a short max-age (say 300 seconds), raise it to one year once every subdomain is confirmed to work over HTTPS, and leave preload until last.
- —Start: Strict-Transport-Security: max-age=300
- —After verification: max-age=31536000; includeSubDomains
- —Preload requirements at hstspreload.org: max-age of at least one year, includeSubDomains and the preload token.
- —The preload list is built into browsers and removal can take months, so a single subdomain without HTTPS (an old mail panel, for example) becomes unreachable.
Browsers only honour HSTS on HTTPS responses; the header is ignored on plain HTTP. Make sure the 301 redirect from HTTP to HTTPS works flawlessly first.
How do you roll out Content-Security-Policy without breaking the site?
The way to roll out Content-Security-Policy without breaking a site is to publish a Content-Security-Policy-Report-Only header first, collect violations from the browser console and a report endpoint for a few weeks, tighten the policy to the sources you really use, and only then switch to enforcing mode. An enforcing CSP from day one can silently break analytics and forms.
Inline scripts are the usual trouble. The gtag snippet placed in the head for Google Ads or Google Analytics is an inline script, and a strict CSP blocks it. You have three options:
- —Nonce: a random value generated per response is added to the script tag and the header. It needs dynamic generation on the server; for sites serving purely static files it is only possible through an edge layer.
- —Hash: the SHA-256 digest of the inline script ('sha256-...') goes into the policy. This suits static and prerendered sites best; change a single character in the script and the hash must be regenerated.
- —'unsafe-inline': the easiest route, but it largely defeats the protection; treat it as a temporary step only.
- —Google tags also need googletagmanager.com, google-analytics.com and the Google Ads domains in script-src, img-src and connect-src; use the current list in Google's official CSP guide.
A sensible starting policy begins with default-src 'self' and then names every external source explicitly. Adding object-src 'none' and base-uri 'self' costs almost nothing and closes two common gaps.
Read more
What do X-Content-Type-Options, Referrer-Policy and Permissions-Policy do?
X-Content-Type-Options, Referrer-Policy and Permissions-Policy are three low-risk headers that almost never break a site. nosniff stops the browser guessing file types, Referrer-Policy limits the URL data sent to external sites, and Permissions-Policy fully disables features the site does not use, such as camera, microphone and geolocation.
- —X-Content-Type-Options: nosniff
- —Referrer-Policy: strict-origin-when-cross-origin (other sites get only your origin, not the full path)
- —Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=()
- —X-XSS-Protection is no longer supported by modern browsers; leave it out or set it to 0.
frame-ancestors or X-Frame-Options?
The modern clickjacking defence is the CSP directive frame-ancestors 'self'; X-Frame-Options: SAMEORIGIN is added for older browsers. When both are present, modern browsers follow frame-ancestors. Note that frame-ancestors does not work in a CSP delivered via a meta tag; it must be sent as an HTTP header.
Where should you set security headers: server, .htaccess or Cloudflare?
Security headers can be set in the web server configuration, in .htaccess on Apache-compatible servers, or at a CDN such as Cloudflare with response header rules. What matters is choosing one place and confirming the header actually appears in the response users receive; defining the same header in two layers can cause duplicates and conflicts.
- —Apache / .htaccess: with mod_headers, Header always set X-Content-Type-Options "nosniff"
- —Nginx: add_header X-Content-Type-Options "nosniff" always;
- —OpenLiteSpeed: header settings in the virtual host context in WebAdmin; .htaccess support is not identical to Apache.
- —Cloudflare: Transform Rules > Modify Response Header under Rules adds headers to every response; HSTS can also be enabled under SSL/TLS > Edge Certificates.
Our own site runs on OpenLiteSpeed behind Cloudflare, and on that host we have seen some .htaccess directives not apply to static files the way you would expect. The lesson: whichever layer you pick, verify against the live response, not the config file.
Local support
What are the most common security header mistakes?
The most common security header mistakes are publishing a strict CSP copied from the internet in enforcing mode without testing, enabling HSTS preload without checking subdomains, and defining the same header on both the server and the CDN so conflicting values reach the browser. Most of these are avoided with a gradual rollout and verification against the live response.
- —Testing CSP only on the homepage and forgetting pages with payments, forms or maps.
- —Adding a new analytics, live-chat or video tool without updating the CSP allow-list.
- —Changing an inline script but leaving the old hash in the policy.
- —Defining the same header in two layers and sending the browser two different values.
- —Adding headers only to HTML responses and skipping error pages (404, 500).
How do you test security headers?
Security headers are tested with free scanners such as securityheaders.com, the MDN HTTP Observatory, the Network tab in browser developer tools and curl -I on the command line. The letter grade is a quick summary, but real verification means seeing the headers on every page type and zero CSP violations in the console.
- —Run curl -I separately against the homepage, an inner page, an image and a JS file.
- —Check response headers in DevTools > Network > the request > Headers.
- —Search the Console for 'Content Security Policy' warnings; during Report-Only they are the raw material for your allow-list.
- —Test form submission, analytics events and conversion tags to confirm nothing broke.
- —Repeat after every major update; a new third-party script can break the CSP.
