A handful of HTTP response headers tell the browser to apply extra protection to your site: always use HTTPS, refuse to be framed by other sites, stop guessing content types, send less referrer data and restrict where scripts may load from. They cost nothing to send, but two of them (HSTS and CSP) can lock users out or break pages if rolled out carelessly.
Definitions below follow the MDN Web Docs reference pages linked in each section.
Strict-Transport-Security (HSTS)
HSTS tells the browser to use only HTTPS for your host from now on, upgrading any future http:// request automatically. max-age is how many seconds the browser remembers this; includeSubDomains extends it to every subdomain; max-age=0 switches it off.
Strict-Transport-Security: max-age=63072000; includeSubDomainsRamp up instead of starting at two years. The HSTS preload site suggests stages of max-age=300, then 604800 (one week), then 2592000 (one month), checking for broken pages at each stage and waiting the full max-age before moving on. Only use includeSubDomains once every subdomain, including internal tools and old hostnames, works over HTTPS.
Preloading (adding preload and submitting the domain so browsers ship it built in) requires a max-age of at least one year, includeSubDomains, and a redirect from HTTP to HTTPS on the same host. The preload site warns that new entries take months to reach stable Chrome, that removal is slow, and that configuration tools should not add preload by default. Treat it as a one-way decision.
X-Content-Type-Options
X-Content-Type-Options: nosniffWith nosniff, the browser blocks a script whose MIME type is not a JavaScript type and a stylesheet that is not text/css, and will not render a text/plain response as HTML. Make sure your server sends correct Content-Type headers before enabling it.
Referrer-Policy
Referrer-Policy: strict-origin-when-cross-originThis is the browser default when no policy is set, according to MDN: full URL for same-origin requests, only the origin for cross-origin requests, nothing when going from HTTPS to HTTP. Setting it explicitly documents your choice; use no-referrer or same-origin if URLs on your site can contain tokens or personal data.
Permissions-Policy
Permissions-Policy: camera=(), microphone=(), geolocation=()An empty list () disables a feature for the page and every embedded frame. List only features you know you do not use. MDN marks Permissions-Policy as limited availability: not every major browser supports it, so treat it as an extra layer, not a guarantee.
Clickjacking: frame-ancestors and X-Frame-Options
The CSP directive frame-ancestors controls which sites may embed your pages in a frame. 'none' forbids all framing; 'self' allows only your own origin. It does not work in a <meta> tag, only as a header. The older X-Frame-Options: DENY or SAMEORIGIN does the same job with fewer options, and many sites send both.
Content-Security-Policy, rolled out in report-only mode
CSP restricts where scripts, styles, images and other resources may come from, which limits the damage of a cross-site scripting bug. It is also the header most likely to break a site, because real pages load scripts from analytics, chat widgets, payment providers and inline code.
Start with Content-Security-Policy-Report-Only. The browser enforces nothing, but reports every violation to an endpoint you define:
Reporting-Endpoints: csp-endpoint="https://example.com/csp-reports"
Content-Security-Policy-Report-Only: default-src 'self'; object-src 'none'; base-uri 'none'; frame-ancestors 'self'; report-to csp-endpoint; report-uri /csp-reportsMDN notes that report-to is preferred and report-uri deprecated, but declaring both helps while support for report-to is incomplete. Collect reports for a few weeks, add the sources you genuinely need, then send the same policy as Content-Security-Policy.
Avoid 'unsafe-inline' for scripts; MDN calls it a defeat of much of CSP's purpose. The recommended approach is a "strict CSP" using a per-response random nonce on each script tag (script-src 'nonce-...'), which needs support from your application or framework.
Nginx and Apache snippets
Nginx, in the server block. The always parameter adds headers to error responses too:
add_header Strict-Transport-Security "max-age=2592000; includeSubDomains" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
add_header Content-Security-Policy "frame-ancestors 'self'" always;One trap: the Nginx documentation says add_header directives are inherited from the previous level only if the current level defines none. A single add_header inside a location block silently drops all server-level headers for that location. Put the headers in a snippet file and include it wherever you add others.
Apache needs mod_headers (sudo a2enmod headers):
Header always set Strict-Transport-Security "max-age=2592000; includeSubDomains"
Header always set X-Content-Type-Options "nosniff"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set Permissions-Policy "camera=(), microphone=(), geolocation=()"
Header always set Content-Security-Policy "frame-ancestors 'self'"Check what is actually sent:
curl -sI https://example.com | grep -i -E 'strict-transport|content-security|x-content-type|referrer-policy|permissions-policy|x-frame'Test a redirect and an error page as well as the home page, and repeat after every proxy, CDN or application change, since any layer can add, remove or duplicate headers.
What you should do
- Add
X-Content-Type-Options,Referrer-Policyandframe-ancestorsnow; they rarely break anything. - Ramp HSTS up in stages, and add
includeSubDomainsorpreloadonly when every subdomain is HTTPS-ready. - Roll out CSP in report-only mode first, collect reports, then enforce; work towards nonces instead of
'unsafe-inline'. - In Nginx, watch for
add_headerinheritance inlocationblocks. - Verify with
curl -sIafter each change.
Sources
- developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Strict-Transport-Security
- hstspreload.org
- developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Content-Type-Options
- developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Referrer-Policy
- developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Permissions-Policy
- developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy/fra...
- developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Frame-Options
- developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CSP
- developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy
- developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy-Rep...
- nginx.org/en/docs/http/ngx_http_headers_module.html
- httpd.apache.org/docs/2.4/mod/mod_headers.html
Comments
0 total · 0 threads