ShieldWave

All articles

Website security headers: what they are and what to ask for

Six HTTP headers that tell the browser how to protect people on your website. What each one does, how to check yours, and a ready brief for your developer.

Radek, ENSOMEDIAPublished 5 min readPo polsku

When someone opens your website, the server sends their browser the page itself, plus a few lines of instructions the visitor never sees. These are HTTP headers. Some of them tell the browser how to protect the people on your site, for example to connect only over an encrypted connection, or not to run scripts from unknown addresses.

Headers cost nothing and you set them once. Even so, many sites do not have them, because the default server setup often leaves them out. If a security check has told you that your site is "missing security headers", this article explains what that means and gives you a ready-made text to send to your developer.

How to see what your site sends

The quickest way is in your own browser. In Chrome, Edge or Firefox, open your website and press F12 (Cmd+Option+I on a Mac). A panel of developer tools opens. Choose the Network tab and reload the page. At the top of the list you will see an entry with your site's address. Click it and look for the Response Headers section.

Ignore the rest of the panel. You are only looking for the names described below.

The second way is a free header checker, such as our security headers check or the HTTP Observatory that Mozilla runs on its MDN site. You type in your address and get a list of headers with a grade. Treat the grade as a hint, not a verdict.

Strict-Transport-Security: always HTTPS

This header, HSTS for short, tells the browser: for a set period, connect to this domain over HTTPS only. It protects visitors on public Wi-Fi, for instance, where someone could try to intercept that first unencrypted connection.

Strict-Transport-Security: max-age=31536000; includeSubDomains

max-age is a time in seconds, here one year. And here is the catch. Once the browser remembers this header, a visitor who hits a certificate problem can no longer click past the warning. includeSubDomains covers every subdomain, so if shop.yourdomain.com or a forgotten test.yourdomain.com has no working HTTPS, they will stop opening.

So you start with a short period, say one day, and extend it once everything works. Do not add preload without thinking it through, because it is very hard to undo.

Content-Security-Policy: a list of trusted sources

This is the most powerful of the headers, and the only one that can really break your site. CSP is a list of the places your pages may load scripts, styles, images and fonts from. If an attacker manages to inject a malicious script, for example through a vulnerable form, the browser refuses to run code from an address that is not on the list.

The trouble is that a typical small business site relies on plenty of outside services: Google Analytics or Tag Manager, the Facebook pixel, a chat window, a map, web fonts, YouTube videos, a payment gateway. Leave one of them off the list and that part of the site stops working. The chat disappears, or customers cannot pay.

That is why CSP should start in a trial mode, under the name Content-Security-Policy-Report-Only. In this mode the browser blocks nothing and only reports what it would have blocked. Your developer collects those reports over a week or two of normal traffic, completes the list, and only then switches the header to enforcing mode. Here is what it can look like:

Content-Security-Policy-Report-Only: default-src 'self'; script-src 'self' https://www.googletagmanager.com; img-src 'self' data: https:

That is only an illustration. The real list depends on what your site uses. On sites with lots of plugins a full CSP can be hard to set up. Even then, a partial policy is better than none.

Four shorter headers

X-Content-Type-Options: nosniff tells the browser to trust the file type the server declares instead of guessing. Without it, a file uploaded as text or an image could in some situations be treated as a script. It rarely breaks anything, as long as the server declares file types correctly.

X-Frame-Options: SAMEORIGIN stops other websites from showing yours inside a frame. It protects against clickjacking: someone loads your page invisibly underneath their own and tricks the visitor into clicking something they cannot see, such as an order button. The newer equivalent is frame-ancestors 'self' in CSP, and you can set both. If your site is meant to appear in a frame elsewhere, for example a booking calendar on a partner's website, your developer has to add that partner's address to frame-ancestors.

Referrer-Policy: strict-origin-when-cross-origin means that when someone clicks a link from your site to another one, that site only learns which domain they came from, not which page. This matters when your page addresses contain something private, such as an order number. Modern browsers already behave this way by default, but setting the header explicitly costs nothing.

Permissions-Policy: camera=(), microphone=(), geolocation=() declares that your site does not use the camera, the microphone or location. Even an injected script then cannot ask the visitor for access to them. If your site has a "find your nearest branch" tool or video consultations, your developer will leave the feature you need switched on.

A ready brief for your developer

You can copy the text below and send it as it is, with your own address.

Please add security headers to yourdomain.com,
on every page, including the admin panel and the shop:

Strict-Transport-Security: max-age=86400; includeSubDomains
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), microphone=(), geolocation=()
Content-Security-Policy-Report-Only: a policy matching
  the scripts and services we actually use

Before enabling includeSubDomains, please check that
every subdomain works over HTTPS. If nothing breaks
within a week, please raise max-age to 31536000.

CSP in Report-Only mode first, for a week or two.
Then please send me the list of reported violations
and let's agree together when to switch to enforcing.

Please also tell me where the headers were set
(server config, .htaccess, Cloudflare or a plugin).

That last request matters. Headers can be set in several places at once, and two different settings for the same header are an easy way to cause confusion the next time something changes. Once they are live, check the headers again and click through the parts that matter most: the contact form, the basket, payment, the chat, the map and logging in to the admin panel.

What headers will not fix

Headers are a second line of defence. They will not fix an outdated plugin or a weak password. They limit the damage when something else fails.

Do not chase the top grade in a checker at any price, either. A site with five simple headers and working payments is in a much better position than one with a perfect CSP that stops customers placing orders.

One last thing: if a tool tells you to add an X-XSS-Protection header, do not treat it as urgent. Most modern browsers ignore it, and current advice is either to leave it out or to set it to 0.