Security Headers: HSTS, CSP and More Explained

Server sending data to a laptop, with small gates holding padlocks and shields checking the packets on the way.
🤖 Image: AI-generated

Published October 18, 2026 · Updated October 18, 2026 · 7 min

In brief

What HTTP security headers like HSTS, Content-Security-Policy and X-Frame-Options do, and how to roll them out and test them safely.

In short: Security headers are lines that the web server sends with every response. They tell the browser, for example, to use only HTTPS, not to load third-party scripts, or not to display the page inside a foreign frame. The most important ones are Strict-Transport-Security (HSTS), Content-Security-Policy (CSP), X-Content-Type-Options, Referrer-Policy and Permissions-Policy. Introduce HSTS and CSP step by step; otherwise, in the worst case, you lock out your own visitors.

What are HTTP security headers?

When a browser requests a page, the server sends a few lines of extra information before the actual content. These HTTP headers contain things like the file type or caching details. Some of them serve security: they set rules that the browser follows. In this way, security headers protect visitors from attacks such as cross-site scripting (XSS) or clickjacking.

Headers don't replace updates or clean code. They are an extra layer of protection that achieves a lot with little effort. The OWASP Secure Headers Project collects recommendations on this. In the OWASP Top 10:2025, a missing header falls under the Security Misconfiguration category, which ranks at number 2 there.

Strict-Transport-Security (HSTS)

HSTS tells the browser: only access this domain via HTTPS. If a visitor later types http:// or clicks an old link, the browser switches to the encrypted connection on its own. An attacker on the same Wi-Fi network can then no longer intercept the connection unencrypted.

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

max-age sets how many seconds the browser remembers the rule; 31536000 equals one year. includeSubDomains extends the rule to all subdomains. According to MDN, the header only takes effect when it is delivered over HTTPS.

Introduce it step by step

First check whether every subdomain really supports HTTPS, including old ones such as a test system or a webmail portal. The operators of the HSTS preload list recommend increasing max-age gradually: first 300 seconds (five minutes), then 604800 (one week), then 2592000 (30 days). Only once everything works should you move to one year. If something goes wrong, set max-age=0 so that browsers forget the rule again.

Be careful with preload

With the preload directive and a submission at hstspreload.org, your domain ends up on a list that is built into browsers. You need a max-age of at least 31536000 and includeSubDomains for this. The catch: inclusion is hard to undo, and removal takes months. Only use preload once you are sure that the whole domain will use HTTPS permanently.

Road splitting in two: a barrier blocks the open lane, a tunnel with a padlock leads on safely.
🤖 Image: AI-generated

Content-Security-Policy (CSP)

The Content Security Policy defines from which sources the browser may load scripts, images, fonts and other resources. If an attacker injects a foreign script, the browser blocks it because its source has not been approved. This makes CSP an effective extra layer of protection against XSS. A simple example:

Content-Security-Policy: default-src 'self'; img-src 'self' https://cdn.example.com; frame-ancestors 'none'; upgrade-insecure-requests

This policy allows content only from your own domain, with images also allowed from a CDN. It forbids embedding the page in foreign frames and makes the browser switch HTTP addresses to HTTPS automatically.

Test mode first

A CSP is often tricky, because modern websites include many third-party services: analytics, maps, videos, fonts, cookie banners. WordPress sites with lots of plugins in particular add up quickly. MDN recommends starting with the Content-Security-Policy-Report-Only header. With it, the browser blocks nothing but reports every violation in the console. You can see which sources are missing, add them to the policy and only then switch it to enforcement.

X-Frame-Options and frame-ancestors

In clickjacking, an attacker loads your website invisibly in an iframe on their own page. Visitors think they are clicking on something harmless and actually trigger an action on your site. The older X-Frame-Options header prevents this:

X-Frame-Options: SAMEORIGIN

With DENY, nobody may embed the page; with SAMEORIGIN, only your own domain may. More comprehensive is the CSP directive frame-ancestors. Its value 'none' works roughly like DENY, and you can also allow individual domains. According to MDN, frame-ancestors does not work in a <meta> tag, only as a real header. Many sites send both variants so that older browsers are protected too.

Web page in a frame, a shield preventing it from being embedded in a foreign frame.
🤖 Image: AI-generated

Other important headers

X-Content-Type-Options

X-Content-Type-Options: nosniff

The value nosniff stops the browser from guessing the file type itself (MIME sniffing). Instead, it sticks to the type the server specifies. This means an uploaded text file won't suddenly be executed as a script. The header has practically no side effects and belongs on every website.

Referrer-Policy

Referrer-Policy: strict-origin-when-cross-origin

When a visitor clicks a link to another website, the browser normally reveals where they came from. The Referrer-Policy controls how much of that is shared. With strict-origin-when-cross-origin, other sites only receive the domain, without the full path and any parameters. According to MDN, this is also the default value when no header is set. If you want to reveal even less, the value is no-referrer.

Permissions-Policy

Permissions-Policy: camera=(), microphone=(), geolocation=()

This header defines which browser features the page and embedded content may use, such as camera, microphone, location or full screen. Empty brackets switch a feature off for everyone, while (self) allows it only for your own domain. The header used to be called Feature-Policy.

X-XSS-Protection

You'll still find this header in many old guides. It controlled an XSS filter that browsers have since removed. MDN advises using a Content-Security-Policy instead. If you do set it, use the value 0.

Where to set security headers

That depends on how your website runs:

  • Web server: On Apache, you can do this in the server configuration or in .htaccess, for example with Header always set X-Content-Type-Options "nosniff". For nginx, the add_header directive does the job.
  • CDN or proxy: Many content delivery networks let you create your own rules for response headers. That's handy if you don't have access to the server.
  • Hosting and website builders: Some providers have a field for custom headers in their customer area. If it's missing, ask their support.
  • CMS plugins: For WordPress, there are extensions for this. The web server is usually the better place, though, because the headers then also apply to images and files.

Set each header in one place only. If both the server and the CDN send a CSP, both apply at the same time, which quickly leads to blocked content.

Checking security headers

Your terminal is the quickest way:

curl -I https://example.com

The response lists all headers of the homepage. Without a terminal, open your browser's developer tools (F12), go to "Network", reload and click the first document. The same information appears under "Response headers". Mozilla also offers the HTTP Observatory on MDN, which rates your headers and suggests improvements. You'll find more checks in our article Check website security.

Common mistakes

  • A long HSTS duration straight away: With includeSubDomains, a subdomain without a valid certificate is blocked for visitors until max-age expires.
  • preload without a plan: The domain stays stuck on the list for months.
  • Enforcing CSP immediately: Forms, maps or the cookie banner stop working.
  • 'unsafe-inline' for scripts: This makes CSP lose much of its XSS protection.
  • Headers only on the homepage: Also check subpages, error pages and redirects.
  • Duplicate headers: Server and CDN set the same rule differently.

Headers barely affect loading speed. If you are working on your server configuration anyway, take a look at our article Improve website speed.

Summary

With X-Content-Type-Options, Referrer-Policy and Permissions-Policy, you can make your website more secure in a few minutes. HSTS and Content-Security-Policy need more care: test first, then tighten gradually. You'll find more about encryption and server settings on our Headers & Encryption topic page.

Sources: MDN Web Docs, articles on Strict-Transport-Security, Content-Security-Policy, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy and X-XSS-Protection; hstspreload.org, notes on preload and rollout; OWASP Secure Headers Project; OWASP Top 10:2025.

Frequently asked questions

What are security headers?

HTTP headers that the server sends to give the browser security rules, for example to use only HTTPS or to load only approved scripts.

Which security headers does every website need?

The basic set is considered to be Strict-Transport-Security, Content-Security-Policy, X-Content-Type-Options, Referrer-Policy and protection against embedding via X-Frame-Options or frame-ancestors.

What is the Content Security Policy?

A rule that defines from which sources the browser may load scripts and other content. It mainly protects against cross-site scripting.

What is clickjacking?

An attack in which a foreign page loads your website invisibly in a frame and tricks visitors into unintended clicks. X-Frame-Options or frame-ancestors prevent this.

What does the Permissions-Policy do?

It defines whether the page and embedded content may use features such as the camera, microphone or location.

How do I check my security headers?

With curl -I https://example.com, in your browser's developer tools under "Network", or with Mozilla's HTTP Observatory.