Security Headers explained

What are security headers?

HTTP security headers are response headers your web server sends to browsers to restrict what the browser is allowed to do with your page. They are a lightweight, high-value defence against a range of common attacks including cross-site scripting (XSS), clickjacking, and protocol downgrade attacks.

They require no changes to your application code — just a small server configuration update.


What to do

Headers checked by ExposureIndex

Strict-Transport-Security (HSTS)

Tells browsers to always use HTTPS for your domain — even if the user types http://.

Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
  • max-age — How long (in seconds) the browser should enforce HTTPS. One year (31536000) is the recommended minimum.
  • includeSubDomains — Also enforce on all subdomains.
  • preload — Optional: allows submission to the browser preload list so HSTS is enforced even on the very first visit.

Content-Security-Policy (CSP)

Restricts which resources (scripts, styles, images, fonts) a page can load and from where. A strict CSP is the most effective browser-side defence against XSS.

A basic starting policy for a simple site:

Content-Security-Policy: default-src 'self'; img-src 'self' data:; font-src 'self'; script-src 'self'; style-src 'self'

CSP is complex and requires tuning for your specific application. Start with Content-Security-Policy-Report-Only to observe violations without blocking, then enforce once confirmed.

If you use external scripts or css like Bootstrap or similar, hosted on their CDN, you will need to include those URL's in the CSP, or else the css or javascripts won't be used. An alternative is to host the external javascripts and css on your webserver.

X-Content-Type-Options

Prevents browsers from guessing ("sniffing") the content type of a response, which can lead to XSS via uploaded files.

X-Content-Type-Options: nosniff

Always set this. It has no downsides.

X-Frame-Options

Prevents your page from being embedded in an <iframe> on another site — the basis of clickjacking attacks.

X-Frame-Options: DENY

Or, if you need your own site to embed pages in iframes:

X-Frame-Options: SAMEORIGIN

Referrer-Policy

Controls how much referrer information the browser sends when a user clicks a link away from your site.

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

Permissions-Policy

Restricts access to browser features like the camera, microphone, geolocation, and payment APIs.

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

Adding headers in common web servers

Nginx:

add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;

Apache:

Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
Header always set X-Content-Type-Options "nosniff"
Header always set X-Frame-Options "DENY"
Header always set Referrer-Policy "strict-origin-when-cross-origin"

Flask (Python):

@app.after_request
def set_security_headers(response):
    response.headers['X-Content-Type-Options'] = 'nosniff'
    response.headers['X-Frame-Options'] = 'DENY'
    response.headers['Referrer-Policy'] = 'strict-origin-when-cross-origin'
    return response

Last updated: March 28, 2026