HTTP Response Headers: a practical, secure-by-default setup
Well-chosen response headers harden your site against common attacks, reduce data leakage, and signal modern best practices to browsers. This guide covers seven headers you can set today—what they do, recommended values, and copy‑paste snippets for Nginx, Apache, and IIS.
- Content-Security-Policy (CSP): Lock down where scripts, styles, images, frames, and connects may load from. Prefer a restrictive
default-src 'self', addbase-uri 'none',object-src 'none', andframe-ancestors 'none'. - Permissions-Policy: Explicitly disable powerful APIs you don’t use (e.g.,
camera=(),geolocation=(),microphone=()). - Referrer-Policy: Minimize cross‑site leakage with
strict-origin-when-cross-origin. - Strict-Transport-Security (HSTS): Enforce HTTPS with
max-age=31536000; includeSubDomains; preloadonce your site is fully HTTPS. - X-Content-Type-Options: Stop MIME sniffing with
nosniff. - X-Frame-Options: Legacy clickjacking defense (
DENYorSAMEORIGIN); modern sites should prefer CSP’sframe-ancestors. - Access-Control-Allow-Origin (ACAO): Only allow specific origins; avoid
*for authenticated content.
1) Content-Security-Policy
CSP is a powerful, declarative allow‑list that controls which sources a page can load. It reduces the blast radius of cross‑site scripting (XSS), malicious third‑party code, and HTML injection.
Starter policy:
Content-Security-Policy:
default-src 'self';
script-src 'self';
style-src 'self' 'unsafe-inline';
img-src 'self' data:;
font-src 'self' data:;
connect-src 'self';
object-src 'none';
base-uri 'none';
frame-ancestors 'none';
Notes: Avoid “smart quotes” in policies; use plain ASCII quotes (') only. Replace any curly quotes pasted from rich text; otherwise browsers will reject the policy. Prefer nonces or hashes for inline scripts instead of 'unsafe-inline' when possible. Use report-to/report-uri for telemetry during rollout.
2) Permissions-Policy
Formerly Feature‑Policy, this header lets you disable or scope access to device features and powerful APIs (camera, mic, sensors, geolocation, etc.). Ship a deny‑by‑default posture, enabling features per page when needed.
Permissions-Policy:
camera=(), microphone=(), geolocation=(), gyroscope=(), magnetometer=(),
usb=(), payment=(), fullscreen=(self)
Values are space‑separated allow‑lists in parentheses. () means “no origins allowed,” while (self) restricts to the current origin. For embedded content, you can allow a partner origin explicitly: geolocation=(self "https://maps.example").
3) Referrer-Policy
Referrer headers leak the origin, path, and query of the page making a request. strict-origin-when-cross-origin is a strong default: it sends the full referrer on same‑origin requests, the origin only on HTTPS → HTTPS cross‑origin, and no referrer on downgrades.
Referrer-Policy: strict-origin-when-cross-origin
Alternatives include no-referrer (most private) and same-origin (restrictive but can break analytics links). Avoid unsafe-url in production.
4) Strict-Transport-Security (HSTS)
HSTS tells browsers to only use HTTPS for a period. Once set, a browser will auto‑upgrade http:// links to https:// and refuse insecure connections for that host.
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
Important: Only enable includeSubDomains and preload after every subdomain serves HTTPS. Preloading submits your domain to a browser‑shipped list and is hard to undo.
5) X-Content-Type-Options
Some browsers attempt to “sniff” content types, which can cause executable content to run from incorrect MIME types. nosniff prevents this behavior for scripts and styles.
X-Content-Type-Options: nosniff
6) X-Frame-Options
Legacy control to prevent clickjacking by blocking framing. Modern CSP uses frame-ancestors, which offers more flexibility. If you need legacy support, set:
X-Frame-Options: DENY
# or
X-Frame-Options: SAMEORIGIN
Do not set both X-Frame-Options and a conflicting frame-ancestors. If both are present, browsers prefer CSP.
7) Access-Control-Allow-Origin (CORS)
Controls which origins may access your resources via cross‑origin requests (XHR, fetch, fonts). For public, unauthenticated assets, you may use *. For any resource that sends or receives credentials (cookies, Authorization), use an explicit origin and echo it selectively.
Access-Control-Allow-Origin: https://example.com
Vary: Origin
When supporting credentials, add Access-Control-Allow-Credentials: true and ensure the value of ACAO is a specific origin (not *).
Implementation snippets
Nginx
add_header Content-Security-Policy "default-src 'self'; object-src 'none'; base-uri 'none'; frame-ancestors 'none'" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
# CORS for a specific origin
add_header Access-Control-Allow-Origin "https://example.com" always;
add_header Vary "Origin" always;
Apache (httpd.conf or .htaccess)
Header always set Content-Security-Policy "default-src 'self'; object-src 'none'; base-uri 'none'; frame-ancestors 'none'"
Header always set Permissions-Policy "camera=(), microphone=(), geolocation=()"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
Header always set X-Content-Type-Options "nosniff"
Header always set X-Frame-Options "DENY"
Header always set Access-Control-Allow-Origin "https://example.com"
Header always set Vary "Origin"
IIS (web.config)
<configuration>
<system.webServer>
<httpProtocol>
<customHeaders>
<add name="Content-Security-Policy" value="default-src 'self'; object-src 'none'; base-uri 'none'; frame-ancestors 'none'" />
<add name="Permissions-Policy" value="camera=(), microphone=(), geolocation=()" />
<add name="Referrer-Policy" value="strict-origin-when-cross-origin" />
<add name="Strict-Transport-Security" value="max-age=31536000; includeSubDomains; preload" />
<add name="X-Content-Type-Options" value="nosniff" />
<add name="X-Frame-Options" value="DENY" />
<add name="Access-Control-Allow-Origin" value="https://example.com" />
<add name="Vary" value="Origin" />
</customHeaders>
</httpProtocol>
</system.webServer>
</configuration>
Tip for IIS & CSP: Ensure all quotes are straight ASCII quotes (' or "). Curly quotes copied from Word or a CMS will break the policy.
Troubleshooting & rollout
- Start in report‑only: For CSP, use
Content-Security-Policy-Report-Onlyto gather violations without breaking pages. - Subresource integrity: Pair external scripts with
integrityhashes andcrossorigin. - Third‑party scripts: If a vendor needs broader allowances, restrict them to a sandboxed subdomain or an isolated page.
- Check console & network: Browser DevTools will show blocked resources; sort by “blocked by” to identify the responsible directive.
- Lint headers: Add automated checks in CI to prevent regressions (e.g., disallow adding
*to ACAO in authenticated routes). - Preload HSTS carefully: Verify all subdomains are HTTPS first; then submit to the preload list.
- Beware duplicates: Multiple conflicting headers (e.g., two CSPs) can cause rejection. Send a single consolidated policy.
Copy‑paste snippets by route
You may want stricter policies on admin routes and looser ones on marketing pages that embed third‑party widgets.
# Example: marketing pages allow specific analytics and video
Content-Security-Policy:
default-src 'self';
script-src 'self' https://www.googletagmanager.com https://www.youtube.com;
img-src 'self' data: https://www.google-analytics.com;
media-src 'self' https://www.youtube.com;
style-src 'self' 'unsafe-inline';
connect-src 'self' https://www.google-analytics.com;
object-src 'none';
base-uri 'none';
frame-ancestors 'none';
Testing checklist
- Verify CSP blocks unexpected inline scripts; migrate essential inline code to nonces/hashes.
- Ensure HSTS is present on HTTPS responses (and not sent on HTTP).
- Confirm
Referrer-Policybehavior by clicking out to another HTTPS domain and inspecting request headers. - Load pages in an iframe to confirm
X-Frame-Optionsorframe-ancestorstake effect. - Exercise CORS with and without credentials to validate ACAO values and
Vary: Origin. - Scan for duplicate/conflicting headers after CDN integrations.
Last updated Aug 17, 2025.
