First: send everyone to HTTPS
Before any header, make sure a visitor who types the plain http:// address is redirected to https://. Headers sent over an unencrypted connection can be changed on the way.
Strict-Transport-Security (HSTS)
Tells the browser to use only HTTPS for this site from now on, so a later visit cannot be downgraded to an unencrypted connection. Start with a short max-age while testing, then use one year:
Strict-Transport-Security: max-age=31536000
Add includeSubDomains only when every subdomain works over HTTPS.
Content-Security-Policy (CSP)
Lists where scripts, styles, images and frames may load from. If an attacker injects a script into a page, the browser refuses to run it unless it comes from an allowed source. It is the most powerful header and the easiest to get wrong, so start with Content-Security-Policy-Report-Only, which reports problems without blocking anything, and switch to the enforcing header once the reports are clean.
X-Content-Type-Options
Stops the browser guessing a file's type, so an uploaded text or image file cannot be run as a script:
X-Content-Type-Options: nosniff
X-Frame-Options
Stops other sites showing yours inside a frame to trick visitors into clicking something (clickjacking). SAMEORIGIN allows only your own pages to frame it. The frame-ancestors setting in a Content-Security-Policy does the same job in newer browsers; many sites send both.
X-Frame-Options: SAMEORIGIN
Referrer-Policy
Controls how much of the page address is sent to other sites when a visitor follows a link. A safe default sends only the site name to other sites:
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy
Turns off browser features the site does not use, so a script that gets in cannot use them either. For a site that uses none of them:
Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=(), usb=()
Where to set them
On the web server (nginx, Apache or IIS), the load balancer or CDN in front of it, or in the hosting control panel. Set them in one place, so they are not duplicated or contradicted. Then check the result with the free security headers checker.
