Security Headers Explained: CSP, HSTS and X-Frame-Options

Security Headers Explained: CSP, HSTS and X-Frame-Options

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.

Tech Contributors

Written by

Tech Contributors

Digital expert at Tech Contributors, sharing insights on web development, SEO, and digital marketing.

View all posts
Share this article:

Frequently Asked Questions

They are instructions your server sends to the visitor’s browser along with each page. They tell the browser to use HTTPS, restrict what scripts can run, block your site from being framed by others, and limit what information is shared. They are an easy extra layer of protection against common attacks.

For most websites, the key ones are Strict-Transport-Security (HSTS), Content-Security-Policy, X-Content-Type-Options, a clickjacking defence (X-Frame-Options or CSP frame-ancestors), and Referrer-Policy. Permissions-Policy is a useful extra.

Yes, especially Content-Security-Policy and HSTS. A strict CSP can block scripts or styles your site needs, and HSTS can lock out subdomains that do not support HTTPS. Test on a staging site first, and introduce CSP in report-only mode before enforcing it.

Not strictly. The frame-ancestors directive in CSP is the modern way to control framing, and having either one is enough to protect against clickjacking. Some sites keep both for older browsers.

Open your browser’s developer tools, go to the Network tab, reload the page, click the main request and read the Response Headers. You can also run curl -I on your address, or use a security scanner that checks and explains them for you.

Got a website problem like this?

Whether it's a new build, a fix, or ongoing maintenance — tell us what you're dealing with and we'll tell you honestly what it'll take.