Response headers tell the browser how strictly to treat your site. They are cheap, high-value hardening and are exactly what Siteward Web Security Scanner checks. Attacks they limit are in 2. Web Application Security.
Headers at a glance
| Header | Protects against | Typical value |
|---|---|---|
| Content-Security-Policy | XSS, data injection, clickjacking | See below |
| Strict-Transport-Security (HSTS) | Downgrade to HTTP, SSL stripping | max-age=31536000; includeSubDomains |
| X-Content-Type-Options | MIME sniffing | nosniff |
| X-Frame-Options | Clickjacking (older; CSP frame-ancestors supersedes) |
DENY or SAMEORIGIN |
| Referrer-Policy | Leaking URLs to other sites | strict-origin-when-cross-origin |
| Permissions-Policy | Unwanted use of camera, mic, geolocation | camera=(), microphone=(), geolocation=() |
| Cross-Origin-Opener-Policy | Cross-window attacks | same-origin |
| Cross-Origin-Resource-Policy | Other sites embedding your resources | same-origin or same-site |
| Cross-Origin-Embedder-Policy | Needed for cross-origin isolation | require-corp |
| Cache-Control | Sensitive pages cached or shared | no-store on private pages |
Also remove information leaks: Server version and X-Powered-By.
Content Security Policy (CSP)
CSP is an allow-list of where content may load from and what may run. If an attacker injects a <script>, the browser refuses to run it because it is not on the list. It is a second line behind output encoding, not a replacement.
Content-Security-Policy:
default-src 'self';
script-src 'self' 'nonce-r4nd0m';
style-src 'self';
img-src 'self' data:;
connect-src 'self';
font-src 'self';
object-src 'none';
base-uri 'self';
form-action 'self';
frame-ancestors 'none';
upgrade-insecure-requests
Directives
| Directive | Controls |
|---|---|
default-src |
Fallback for any fetch type not listed |
script-src |
JavaScript sources |
style-src |
CSS sources |
img-src / font-src / media-src |
Images, fonts, audio and video |
connect-src |
fetch, XHR, WebSocket targets |
frame-src |
What you may embed |
frame-ancestors |
Who may embed you (clickjacking) |
object-src |
Plugins; set to 'none' |
base-uri |
Stops injected <base> tags |
form-action |
Where forms may submit |
upgrade-insecure-requests |
Upgrade http:// sub-resources to HTTPS |
report-to / report-uri |
Where violation reports are sent |
Source values
| Value | Meaning |
|---|---|
'self' |
Same origin only |
'none' |
Nothing allowed |
https://cdn.example.com |
A specific host |
'nonce-...' |
Only tags carrying that per-response random value |
'sha256-...' |
Only inline code with that exact hash |
'strict-dynamic' |
Trust scripts loaded by an already-trusted script |
'unsafe-inline' / 'unsafe-eval' |
Defeat the protection; avoid |
Nonces: allow inline scripts safely
The server generates a fresh random value per response and puts it in both the header and the tag:
<script nonce="r4nd0m">/* allowed */</script>
<script>/* blocked: no nonce */</script>
A nonce must be unpredictable and unique per response. A static "nonce" is worthless.
Rolling it out without breaking the site
flowchart LR
A[Start: Report-Only] --> B[Collect violation reports]
B --> C[Fix inline code, add needed sources]
C --> D{No unexpected violations?}
D -- no --> B
D -- yes --> E[Enforce with Content-Security-Policy]
E --> F[Keep monitoring reports]
Content-Security-Policy-Report-Only: default-src 'self'; report-to csp-endpoint
Report-Only logs violations without blocking, so you can tighten safely. Browser developer tools show the same violations in the console.
Common sources of breakage: inline <script> and onclick= handlers, inline styles, analytics and tag managers, CDN fonts and images, embedded video. Move inline code to files, or use nonces.
HSTS
Tells the browser to use HTTPS only for your domain for the stated time.
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
- Start with a short
max-age, confirm every subdomain works over HTTPS, then raise it to a year preloadsubmits the domain to browsers' built-in list; hard to undo, so be sure first- Only effective once the first HTTPS visit happens; redirect HTTP to HTTPS too
Cookie Flags
| Flag | Effect |
|---|---|
Secure |
Sent only over HTTPS |
HttpOnly |
Not readable by JavaScript (limits XSS token theft) |
SameSite=Lax / Strict |
Limits cross-site sending (CSRF defence) |
__Host- prefix |
Forces Secure, path /, no Domain: tightest scope |
Set-Cookie: __Host-session=abc123; Path=/; Secure; HttpOnly; SameSite=Lax
CORS (not a security header you add to protect, but often misused)
CORS relaxes the browser's same-origin rule for chosen sites.
- Never combine
Access-Control-Allow-Origin: *with credentials - Do not reflect the request
Originback without checking an allow-list - CORS does not stop a server being called directly; it only governs browsers
Setting Them
Nginx
add_header Strict-Transport-Security "max-age=31536000; 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 "default-src 'self'; frame-ancestors 'none'; object-src 'none'; base-uri 'self'" always;
server_tokens off;
Static hosts (Cloudflare Pages, Netlify)
A _headers file in the output folder:
/*
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), microphone=(), geolocation=()
Content-Security-Policy: default-src 'self'; frame-ancestors 'none'
Django
Built-in settings cover HSTS, cookies, nosniff, referrer policy and frame options (see the checklist in 2. Web Application Security). For CSP use the django-csp package (recent Django releases also add built-in CSP support; check the docs for your version).
At the edge
CDNs and gateways can add headers for every response: CloudFront response headers policies (5. AWS Networking), Azure Front Door rule sets (3. Azure Networking).
Checking Your Work
$ curl -sI https://example.com | grep -iE 'strict-transport|content-security|x-content-type|referrer|permissions|x-frame'
- Run a passive scan with Siteward Web Security Scanner
- Online graders: Mozilla Observatory and securityheaders.com (public sites only)
- Browser DevTools, Network tab: look at the response headers; Console: CSP violations
Aim for: HTTPS with HSTS, a CSP without 'unsafe-inline' for scripts, nosniff, a referrer policy, frame-ancestors, and cookies flagged Secure; HttpOnly; SameSite.