HTTP security headers: which ones does your website need?

What HSTS, CSP, X-Frame-Options and the other security headers are, which attack each one stops, and in what order to switch them on without breaking anything.

By , Full stack developer

On this page

Every time someone opens a page on your site, the server sends more than the HTML. First comes a block of headers: lines of text the visitor never sees, telling the browser how to treat whatever follows. Some deal with caching or language. The security ones give instructions such as “never load this site over http”, “do not let another page put me in a frame” or “do not run scripts from origins I have not approved”.

None of them fixes a badly built site. What they do is close doors the browser leaves open by default, and they are among the few security improvements you configure once, on the server, without touching the site’s code.

Why the browser does not protect itself

Because the web was born permissive and browsers still have to display sites from twenty years ago. If they blocked third-party frames or inline scripts tomorrow, millions of pages would break. So the decision is handed to whoever serves the site: you state which restrictions your site accepts and the browser enforces them.

That is also why a missing header produces no visible error. The site works exactly the same; it is simply exposed to attacks that one line of configuration would have closed.

HSTS: https, always

Strict-Transport-Security tells the browser that, for a set period, it must only reach your domain over https. Even if someone types the address with http or follows an old link, the browser switches to https before sending the request.

The difference from a plain http-to-https redirect is subtle but matters: with the redirect, the first request still goes out unencrypted, and on public wifi someone could intercept it. With HSTS, from the second visit onwards that request simply never happens.

A typical value is max-age=31536000; includeSubDomains: one year, subdomains included.

What is the preload list?

HSTS has a gap: the very first visit. The browser cannot know your domain requires https until it has been there once. The preload list, built into browsers, closes that gap. To join it you need a max-age of at least a year, includeSubDomains, the preload directive, and the whole domain working over https.

Think it through first: leaving the list takes months, and any subdomain still relying on http (an old admin panel, an internal tool) will stop opening.

CSP: where code may come from

Content-Security-Policy is the most powerful header and the most delicate. It lists the origins allowed to supply scripts, styles, images, fonts and frames. If someone manages to inject code into your page, through a poorly filtered form or a vulnerable plugin, the browser refuses to run it because it does not come from an approved origin.

The catch is that a real site loads things from many places: analytics, chat, maps, video, fonts. A policy that is too strict blocks them all; one that is too loose (default-src *, or 'unsafe-inline' in script-src) exists but protects nothing. That is why a CSP is built in stages, never in one go.

X-Frame-Options and frame-ancestors: against clickjacking

Clickjacking means loading your site inside an invisible frame on another page and tricking the user into clicking somewhere they do not expect. If your site has a dashboard with “confirm” or “delete” buttons, the risk is real.

X-Frame-Options: DENY (or SAMEORIGIN, if you use frames yourself) prevents it. The modern approach is the frame-ancestors directive inside the CSP, which supersedes it in current browsers. Having both does no harm: the older header covers older browsers.

The easy three: nosniff, Referrer-Policy and Permissions-Policy

X-Content-Type-Options: nosniff stops the browser from “guessing” a file’s type. Without it, a file uploaded as an image could end up being interpreted as a script. It has a single valid value and almost never breaks anything.

Referrer-Policy controls how much of your page’s URL is sent to the sites you link to. If your URLs carry data (an order number, an internal search term), there is no reason for it to reach third parties. Current browsers already apply a cautious default, but declaring strict-origin-when-cross-origin pins the behaviour down everywhere.

Permissions-Policy switches off features your site does not use: camera, microphone, location, payments. If a third-party script tries to request the visitor’s location, the browser blocks it. A value such as camera=(), microphone=(), geolocation=() is enough for most company sites.

Headers you can do without: Server, X-Powered-By and X-XSS-Protection

Some headers do not protect anything; they give things away. Server: Apache/2.4.41 or X-Powered-By: PHP/7.4 tell anyone which software you run and which version, saving work for anyone hunting known flaws. Removing the version, or the whole header, is a one-minute change.

X-XSS-Protection is a leftover: modern browsers ignore it or have removed it. If it is there, it can go without consequence.

Where to start without breaking the site

Order matters more than ambition. A sensible sequence:

  1. The harmless ones first: X-Content-Type-Options, Referrer-Policy, Permissions-Policy and X-Frame-Options. They rarely affect anything and close several fronts at once.
  2. HSTS with a short max-age, minutes or hours. Check that the whole site and its subdomains work over https and that the http-to-https redirect is a single hop. Then raise it to a year.
  3. CSP in report-only mode (Content-Security-Policy-Report-Only). The browser reports what it would block without blocking it. Review the reports, add the legitimate origins and, once they stop appearing, switch to the enforcing header.
  4. HSTS preload, if it fits, once HSTS has been stable for a while and you have an inventory of your subdomains.

Where you set them depends on your hosting: in the hosting control panel, in Apache’s .htaccess, in the Nginx configuration, in a _headers file on platforms such as Cloudflare Pages or Netlify, or in your CDN’s rules. WordPress also has plugins that add them, though the server is the better place, since it does not depend on the plugin staying installed.

Checking yours

Our security headers checker reads the headers any URL returns, grades them one by one with an explanation of what is right, what is missing and what is misconfigured, and checks whether the domain is already on the HSTS preload list. It is useful before and after each change: record the starting point, apply one stage, look again.

Check an inner page too, not just the home page. Some servers apply different headers depending on the path, and it is common to find a well-protected home page and nothing at all on the rest of the site.


Security headers are no substitute for keeping the site up to date, but they are a cheap and lasting layer. An afternoon of configuration, done in the right order, closes off attacks that would otherwise depend on nobody trying them.

Shall we talk about continuous support & evolution?

Tell us your case and we come back with a first read on scope within one business day.

We reply within one business dayBogotá · UTC−5

Message us on WhatsApp (opens in a new tab)