Every time someone visits your website, your server sends back more than the page itself. It also sends a set of hidden instructions called HTTP headers, and some of them tell the visitor’s browser how to behave safely. These are security headers. They cost nothing to add, they make common attacks harder, and yet a large number of websites are missing them. Here is what the most important ones do, in plain English.
What Are Security Headers?
A security header is a rule your server hands to the browser, such as “only connect to me over HTTPS” or “do not let other sites display my pages inside a frame.” The browser follows the rule, which blocks entire categories of attacks before they can work. Security headers are not a replacement for updates, strong passwords or good code, but they add an extra layer of protection that is easy to switch on.
Strict-Transport-Security (HSTS)
HSTS tells the browser to always use HTTPS for your site, even if someone types http:// or clicks an old link. This stops attackers from downgrading a visitor’s connection to an unencrypted one. A typical value is:
Strict-Transport-Security: max-age=31536000; includeSubDomains
The max-age value is how long, in seconds, the browser remembers the rule (31536000 is one year). Only add includeSubDomains if every subdomain of your site works over HTTPS, and consider starting with a short max-age while you test.
Content-Security-Policy (CSP)
CSP is the most powerful and the trickiest header. It lists which sources the browser may load scripts, styles, images and frames from. If an attacker manages to inject a malicious script into one of your pages, a good CSP stops the browser from running it. A simple example:
Content-Security-Policy: default-src 'self'; object-src 'none'; frame-ancestors 'self'
Having a CSP is not enough on its own. A policy that allows 'unsafe-inline' scripts or a wildcard (*) for scripts gives much weaker protection, because it still lets injected code run. CSP also breaks sites easily, especially WordPress sites where plugins add inline scripts. The safe way to roll it out is to start with Content-Security-Policy-Report-Only, which logs problems without blocking anything, then tighten it gradually.
X-Frame-Options
This header controls whether other websites can display your pages inside a frame. Without it, an attacker could load your site invisibly inside their own page and trick visitors into clicking buttons they cannot see, an attack called clickjacking. The usual value is SAMEORIGIN (only your own site may frame your pages) or DENY (nobody may). The modern replacement is the frame-ancestors directive inside CSP, so you need one of the two, not necessarily both.
X-Content-Type-Options
Set to nosniff, this stops browsers from guessing a file’s type and potentially running something that was uploaded as an image or text file as if it were a script. It is one line with almost no downside:
X-Content-Type-Options: nosniff
Referrer-Policy
When someone clicks a link from your site to another site, the browser can tell the other site which page they came from. Referrer-Policy controls how much of that address is shared, which matters when URLs contain things like search terms or account identifiers. A sensible default is:
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy
This header lets you switch off browser features your site never uses, such as the camera, microphone or location. If a script is ever compromised, it cannot ask for access you have already disabled:
Permissions-Policy: camera=(), microphone=(), geolocation=()
Old Headers You Can Ignore
X-XSS-Protection, Public-Key-Pins and Expect-CT are deprecated. If your server sends them, they are not harmful, but they no longer provide meaningful protection and can be removed.
How to Check Which Headers Your Site Sends
In Chrome or Edge, open your site, press F12, go to the Network tab, reload the page, click the first request (your page) and look under Response Headers. If you are comfortable with a terminal, curl -I https://yourdomain.com prints the headers directly. Our free website security scanner also checks them for you, reads what your CSP actually allows, and explains in plain English what is missing and why it matters.
How to Add Security Headers
On Nginx, add these to your server block:
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
On Apache (with mod_headers enabled):
Header always set X-Content-Type-Options "nosniff"
Header always set X-Frame-Options "SAMEORIGIN"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
If you use a CDN such as Cloudflare, you can usually add headers through its rules instead, and on WordPress several security plugins offer header settings. Add CSP separately, in report-only mode first, because it needs to match what your site actually loads. Always test on a staging copy before changing a live site.
A Missing Header Is Not Always a Vulnerability
Headers are defence in depth. A missing one does not mean your site is hacked or definitely vulnerable, and some are more relevant to your setup than others. That is why a good scan explains the risk instead of just marking everything red. Headers are also only one part of the picture. For the other basics, read our guides on how to check if your website is secure, exposed .env and backup files, and SPF, DKIM and DMARC. If you would like help adding headers to your site without breaking anything, talk to our team.