Where to set them

OpenLiteSpeed exposes per-vhost "Contexts" that can inject response headers on a path. Setting headers at the vhost level means they apply to every response from that site, which is what you want.

  1. Open WebAdmin at https://your-server:7080.
  2. Go to Virtual Hosts → (your vhost) → Context.
  3. Click AddStatic.
  4. URI: /. Accessible: Yes. Leave Location and Index Files empty.
  5. In Header Operations, paste the header block below.
  6. Save, then Graceful Restart.

The header set

Strict-Transport-Security: max-age=31536000; includeSubDomains
Content-Security-Policy: default-src 'self'; img-src 'self' data: https:; style-src 'self' 'unsafe-inline'; script-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'self'
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: geolocation=(), microphone=(), camera=(), payment=()
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Resource-Policy: same-origin
Content-Security-Policy: test before deploying

CSP is strict by nature. The moment you set a restrictive policy, any script or style loaded from a domain that isn't explicitly allowed stops working. If you use Google Fonts, a CDN, an analytics script, or a YouTube embed, your policy has to name them. Start by deploying in report-only mode first — add the header Content-Security-Policy-Report-Only instead of Content-Security-Policy, point report-uri at an endpoint you can read, and let real traffic tell you what you'd break before you break it.

What each header actually does

Strict-Transport-Security (HSTS) — once a browser sees this on a site, it refuses to connect over plain HTTP for the stated duration. One year (31536000 seconds) is the standard. includeSubDomains extends it to every subdomain, which is what you want only if all subdomains are HTTPS-only. Add preload and submit to hstspreload.org to have the browser bake this in from first contact — but only once you're confident; removal from the list takes months.

Content-Security-Policy (CSP) — an allow-list for the origins a page can load scripts, styles, images, and other resources from. The single biggest defence against XSS: even if an attacker manages to inject a <script src="evil.com/x.js">, the browser refuses to fetch it because evil.com isn't in your script-src. The policy above is conservative and will need loosening for most real sites — the trade-off is that every loosening is a documented decision.

X-Content-Type-Options: nosniff — tells browsers to trust the Content-Type header you sent instead of guessing from the byte content. Stops a whole class of attacks where an attacker uploads a file that looks like an image but is actually executable JavaScript.

X-Frame-Options: SAMEORIGIN — the old way to block clickjacking (another site loading yours in an <iframe> and tricking a user into clicking through). CSP's frame-ancestors is the modern replacement and supersedes this header, but both are cheap to ship and older browsers only understand X-Frame-Options.

Referrer-Policy: strict-origin-when-cross-origin — when a user follows a link from your site to a different origin, only the origin (not the full URL) is sent as the Referer. Stops you from leaking internal URLs with query-string secrets to everywhere you link.

Permissions-Policy (formerly Feature-Policy) — disables browser capabilities you don't use. If your site has no reason to request geolocation, camera, or microphone, denying them at the policy level means a compromised third-party script can't prompt the user either.

Cross-Origin-Opener-Policy and Cross-Origin-Resource-Policy — the newer "site isolation" headers. They turn on process-level isolation in the browser, which blocks the Spectre-style side-channel attacks that were the reason site isolation exists in the first place. On by default in Chrome since 2022, but still worth requesting explicitly.

Verify

Both of these give useful A-through-F grades and list what's missing:

A sensible starting target is A+ on securityheaders.com. Achieving it without breaking anything is a useful forcing function: it makes you audit every third-party script your site loads.