They are not a firewall and they do not repair broken authorization, vulnerable APIs, SQL injection, leaked credentials or outdated dependencies. Security headers are a defence-in-depth layer: they reduce the browser attack surface and restrict what can happen after another weakness has already been exploited.
In 2026, it is useful to separate three categories:
- Baseline headers that make sense for most websites.
- Architecture-dependent headers that may break OAuth, payments, CDNs, iframes or PDF delivery when copied without analysis.
- Obsolete headers that should not be enabled simply to improve the score of an outdated scanner.
TL;DR: start with a carefully designed Content Security Policy, HSTS after HTTPS is fully deployed,
X-Content-Type-Options: nosniff, an explicitReferrer-Policyand a restrainedPermissions-Policy. Use CSPframe-ancestorsas the primary anti-framing control and retainX-Frame-Optionsonly as a compatibility layer. Deploy COOP, COEP and CORP only after reviewing cross-origin integrations. Remove or disableX-XSS-Protection,Expect-CT, HPKP and the oldReport-Toheader.
Recommendations and compatibility were last verified on 23 July 2026.
The main headers at a glance
| Header | Typical recommendation | What it limits | Use everywhere? |
|---|---|---|---|
Content-Security-Policy |
application-specific policy, preferably nonce- or hash-based | XSS, injection, unapproved resource origins and framing | yes for HTML, after testing |
Strict-Transport-Security |
max-age=31536000; includeSubDomains; add preload only after review |
HTTP downgrade and bypassing certificate errors | yes when the whole domain is HTTPS-ready |
X-Content-Type-Options |
nosniff |
MIME sniffing and some MIME confusion | yes |
Referrer-Policy |
strict-origin-when-cross-origin or stricter |
URL path and query leakage | yes |
Permissions-Policy |
disable unused capabilities such as camera=(), microphone=(), geolocation=() |
use of selected browser APIs by documents and frames | usually, after feature testing |
CSP frame-ancestors |
'none', 'self' or explicit origins |
clickjacking and unwanted framing | yes for HTML documents |
X-Frame-Options |
DENY or SAMEORIGIN for compatibility |
framing in older implementations | often, alongside frame-ancestors |
Cross-Origin-Opener-Policy |
often same-origin, unless popup relationships are needed |
some XS-Leaks and window.opener access |
architecture-dependent |
Cross-Origin-Embedder-Policy |
require-corp or credentialless, only deliberately |
embedding cross-origin resources without permission | no, not automatically |
Cross-Origin-Resource-Policy |
same-origin, same-site or cross-origin per resource |
unwanted no-CORS cross-origin reads | resource-dependent |
Cache-Control |
no-store for highly sensitive responses; private for personalized content |
storage in browser and intermediary caches | for sensitive responses |
Set-Cookie |
Secure; HttpOnly; SameSite=Lax/Strict as appropriate |
theft and inappropriate cross-site sending of cookies | for session cookies |
Reporting-Endpoints |
endpoint for CSP/COOP/COEP reports | policy observability | optional |
X-XSS-Protection |
omit or set to 0 |
legacy XSS filter | do not enable |
Expect-CT |
remove | obsolete Certificate Transparency mechanism | no |
Public-Key-Pins |
do not use | historical certificate pinning | no |
OWASP treats response headers as valuable hardening controls, while stressing that each value must match the response type and the application architecture. [1]
A minimal starting point
This is a starting template, not a universal copy-and-paste answer:
Content-Security-Policy: default-src 'self'; base-uri 'self'; object-src 'none'; frame-ancestors 'none'; form-action 'self'; img-src 'self' data: https:; font-src 'self'; style-src 'self'; script-src 'self'; connect-src 'self'; upgrade-insecure-requests
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), microphone=(), geolocation=()
X-Frame-Options: DENY
This policy blocks inline scripts and styles, unlisted resource origins, framing, forms submitted to other origins, camera/microphone/geolocation access and HTTP navigation after HSTS has been stored.
If the site uses external fonts, analytics, maps, payment widgets, OAuth, tag managers, third-party APIs or inline code, the policy must be adapted.
Content-Security-Policy: the most powerful and difficult header
Content-Security-Policy defines where the browser may load scripts, styles, images, fonts, frames and network connections from. [2] A correct CSP can substantially reduce the impact of XSS and data injection, but it does not replace input validation, output encoding and safe DOM APIs.
Core directives
| Directive | Purpose | Common starting value |
|---|---|---|
default-src |
fallback for resource types without a dedicated directive | 'self' |
script-src |
permitted scripts | nonce or hashes, optionally 'self' |
style-src |
permitted styles | 'self', nonce or hashes |
img-src |
images | 'self' data: https: |
font-src |
fonts | 'self' and explicit CDN origins |
connect-src |
Fetch, XHR, WebSocket and EventSource | your API and explicit endpoints |
frame-src |
frames embedded by the page | only required origins |
frame-ancestors |
which parents may embed this page | 'none' or 'self' |
form-action |
valid form destinations | 'self' or explicit endpoints |
base-uri |
permitted <base> URLs |
'self' or 'none' |
object-src |
<object> and <embed> plugins |
'none' |
upgrade-insecure-requests |
rewrite HTTP subresources to HTTPS | no value |
Allowlists versus a strict CSP
A domain allowlist can become fragile. A trusted CDN may host many unrelated files, and a third-party loader may pull in additional dependencies dynamically. web.dev recommends a strict CSP based on nonces or hashes; strict-dynamic allows scripts loaded by a trusted script to inherit trust. [3]
Example nonce policy:
Content-Security-Policy:
default-src 'self';
base-uri 'self';
object-src 'none';
frame-ancestors 'none';
form-action 'self';
script-src 'nonce-{RANDOM_NONCE}' 'strict-dynamic';
style-src 'self' 'nonce-{RANDOM_NONCE}';
img-src 'self' data: https:;
font-src 'self';
connect-src 'self' https://api.example.com;
upgrade-insecure-requests
Matching HTML:
<script nonce="{RANDOM_NONCE}">
window.__APP_CONFIG__ = { apiUrl: "https://api.example.com" };
</script>
A nonce must be unpredictable, unique for every HTML response, present in the header and trusted elements, and generated by the server. A constant nonce stored in configuration does not provide the intended protection. For static pages that cannot generate a new value per request, hashes can be more practical.
Avoid unsafe-inline and unsafe-eval
'unsafe-inline' in script-src permits inline JavaScript and greatly weakens the XSS protection. 'unsafe-eval' enables APIs that execute strings as code, including eval() and the Function constructor.
Do not add them merely to silence console violations. Instead:
- move inline code into external files;
- use a nonce or hash;
- identify the dependency requiring
eval; - look for a different build or library configuration.
Trusted Types
A policy such as:
Content-Security-Policy: require-trusted-types-for 'script'; trusted-types app-policy
can require typed values at selected DOM XSS sinks such as innerHTML. Since February 2026, require-trusted-types-for is available in current major browsers, although older releases may not support it. [4]
Trusted Types is an advanced defence. The application must define safe transformation policies, and frameworks and dependencies must be compatible. Adding the header alone is not sufficient.
Start with Report-Only
A safer rollout begins with:
Content-Security-Policy-Report-Only: default-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'none'; report-to csp-endpoint
Reporting-Endpoints: csp-endpoint="https://example.com/security/csp-reports"
Content-Security-Policy-Report-Only reports violations without blocking resources, allowing missing origins and broken flows to be identified before enforcement. [5] Reporting-Endpoints replaces the deprecated Report-To header and should be preferred for current deployments. [6]
A reporting endpoint should enforce payload size limits, rate limiting, safe logging, output encoding and a short, purpose-specific retention period. CSP reports can contain URLs and resource details.
Strict-Transport-Security: HTTPS with no downgrade path
HSTS tells the browser that a host must be contacted only over HTTPS. Once stored, the browser upgrades future HTTP attempts and prevents users from bypassing some certificate errors. [7]
Strict-Transport-Security: max-age=31536000; includeSubDomains
max-age=31536000stores the policy for one year.includeSubDomainscovers subdomains.preloadindicates an intention to join the browser preload list.
Do not start with preload
A wrong HSTS deployment can lock legitimate users out. [8] An old HTTP-only subdomain, expired certificate or third-party service under your domain can make includeSubDomains and a long max-age dangerous.
A cautious rollout can use:
5 minutes → 1 day → 1 week → 1 month → 1 year
At each stage test the apex domain, active subdomains, wildcard and SAN certificates, legacy services, admin panels, APIs and partner endpoints.
HSTS preload requires a valid certificate, HTTP-to-HTTPS redirects, a sufficiently long max-age, includeSubDomains and the preload token. [9] Removal may take weeks because the list is shipped inside browsers.
X-Content-Type-Options: stop MIME guessing
X-Content-Type-Options: nosniff
This tells the browser to respect the declared Content-Type rather than reinterpret a response as another type. Scripts and styles may be blocked when their MIME type does not match what is expected. [10]
It does not replace correct server configuration. Responses still need accurate values:
Content-Type: text/html; charset=utf-8
Content-Type: text/css; charset=utf-8
Content-Type: application/javascript; charset=utf-8
Content-Type: application/json; charset=utf-8
Content-Type: image/avif
This is particularly important for user uploads, download endpoints, dynamically generated files, object storage, CDNs and error responses that might return HTML instead of JSON.
Referrer-Policy: reduce URL leakage
Referrer-Policy controls how much of the current page URL may be sent in the Referer header when requesting another resource.
A sensible default is:
Referrer-Policy: strict-origin-when-cross-origin
It sends the full URL for same-origin requests, only the origin for cross-origin HTTPS requests, and no referrer on an HTTPS-to-HTTP downgrade. [11]
Stricter alternatives include:
Referrer-Policy: no-referrer
or:
Referrer-Policy: same-origin
The choice may affect analytics, affiliate systems and payment providers. Secrets, tokens, email addresses and personal data should not be placed in URLs in the first place.
Clickjacking protection: frame-ancestors and X-Frame-Options
The most flexible control is CSP:
Content-Security-Policy: frame-ancestors 'none'
or:
Content-Security-Policy: frame-ancestors 'self' https://portal.partner.example
frame-ancestors defines which parents may embed a document in <frame>, <iframe>, <object> or <embed>. [12]
For compatibility, add:
X-Frame-Options: DENY
or:
X-Frame-Options: SAMEORIGIN
MDN recommends frame-ancestors for modern deployments because it is more expressive. [13] ALLOW-FROM is obsolete and may cause current browsers to ignore the header. X-Frame-Options must be an HTTP header; a <meta http-equiv> version has no effect. [13]
Permissions-Policy: disable capabilities the site does not use
Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=()
The header controls access by a document and its frames to selected browser capabilities, including camera, microphone and geolocation. [14]
Allow the current origin:
Permissions-Policy: geolocation=(self), camera=(), microphone=()
Or permit an explicit origin:
Permissions-Policy: geolocation=(self "https://maps.example")
Do not copy an enormous list of every known directive. Individual directives have different browser support and some remain experimental. [14] Identify the capabilities the application uses, then explicitly disable relevant unused ones.
COOP, COEP and CORP: cross-origin isolation is not a universal default
These headers are related but solve different problems.
Cross-Origin-Opener-Policy
Cross-Origin-Opener-Policy: same-origin
COOP controls whether documents opened through navigation or window.open() share a browsing context group. same-origin separates the document from cross-origin openers and mitigates some XS-Leaks. [15]
It can break OAuth popups, payment windows and integrations relying on window.opener. In some cases this is more appropriate:
Cross-Origin-Opener-Policy: same-origin-allow-popups
Cross-Origin-Embedder-Policy
Cross-Origin-Embedder-Policy: require-corp
COEP requires cross-origin no-cors resources to grant permission through CORP or to be fetched with CORS. Missing headers can block images, fonts, scripts, frames and other third-party assets. [16]
An alternative is:
Cross-Origin-Embedder-Policy: credentialless
This permits some no-cors resources without explicit CORP while stripping credentials such as cookies.
Cross-Origin-Resource-Policy
Cross-Origin-Resource-Policy: same-origin
CORP is a policy on the resource itself and may use:
same-origin,same-site,cross-origin.
It is not a replacement for CORS. MDN also documents a Chrome issue involving partial PDF rendering under some CORP deployments, so the header should not be applied globally without tests. [17]
The pair:
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
enables cross-origin isolation required by selected advanced APIs, including full SharedArrayBuffer access. Do not deploy it solely for a scanner score.
Secure cookies and caching
Set-Cookie and Cache-Control are not always listed as security headers, but they directly protect sessions and sensitive data.
Session cookie
Set-Cookie: __Host-session=RANDOM_VALUE; Path=/; Secure; HttpOnly; SameSite=Lax
Securerestricts transmission to HTTPS.HttpOnlyprevents JavaScript access.SameSitelimits cross-site sending.__Host-requiresSecure,Path=/and noDomain, binding the cookie more tightly to the host. [18]
SameSite=Strict is stronger but may disrupt returns from login or payment providers. SameSite=None requires Secure and should be used only for a genuinely cross-site cookie. [18]
Sensitive responses
Cache-Control: no-store
This asks private and shared caches not to store the response. Personalized content that may be cached in the browser but not by intermediaries can use:
Cache-Control: private, no-cache
MDN recommends explicitly marking personalized responses private to avoid accidental shared caching. [19]
Reduce technology disclosure
Headers such as:
Server: nginx/1.24.0
X-Powered-By: PHP/8.4
X-AspNet-Version: 4.0.30319
make fingerprinting easier. Removing them does not hide the stack from a determined attacker, but avoids direct version disclosure.
- remove
X-Powered-By; - disable framework version headers;
- limit detail in
Server; - do not publish a deliberately false version;
- do not mistake obscurity for patching.
OWASP recommends removing X-Powered-By and limiting Server, while noting that technologies can still be inferred in other ways. [1]
Obsolete headers and misleading advice
X-XSS-Protection
Do not enable:
X-XSS-Protection: 1; mode=block
OWASP warns that legacy XSS filters can create vulnerabilities in otherwise safe pages. [1] Omit the header or disable it explicitly:
X-XSS-Protection: 0
Use CSP, output encoding and safe APIs instead.
Expect-CT
Expect-CT is obsolete in practice because current clients require Certificate Transparency for modern certificates. MDN describes it as largely obsolete since June 2021. [20]
Public-Key-Pins
HPKP should not be deployed. A wrong pin could lock users out for a long time and the header has been removed from modern browsers. Prefer correct TLS, automated renewal, CAA, certificate monitoring and carefully reviewed HSTS preload.
Report-To
The old:
Report-To: { ... }
is deprecated. Use:
Reporting-Endpoints: csp="https://example.com/reports/csp"
Access-Control-Allow-Origin: *
CORS is not a hardening header bundle to add globally. Access-Control-Allow-Origin relaxes the Same-Origin Policy. Credentialed API responses cannot safely combine wildcard * with authenticated cross-origin access. Allow only required origins and validate them server-side.
Server configuration examples
Nginx
add_header Content-Security-Policy "default-src 'self'; base-uri 'self'; object-src 'none'; frame-ancestors 'none'; form-action 'self'; img-src 'self' data: https:; style-src 'self'; script-src 'self'; connect-src 'self'; upgrade-insecure-requests" always;
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 X-Frame-Options "DENY" always;
always matters because it keeps the headers on error and non-standard status responses.
Apache
<IfModule mod_headers.c>
Header always set Content-Security-Policy "default-src 'self'; base-uri 'self'; object-src 'none'; frame-ancestors 'none'; form-action 'self'; img-src 'self' data: https:; style-src 'self'; script-src 'self'; connect-src 'self'; upgrade-insecure-requests"
Header always set Strict-Transport-Security "max-age=31536000; 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 X-Frame-Options "DENY"
</IfModule>
PHP
<?php
header("Content-Security-Policy: default-src 'self'; base-uri 'self'; object-src 'none'; frame-ancestors 'none'; form-action 'self'; img-src 'self' data: https:; style-src 'self'; script-src 'self'; connect-src 'self'; upgrade-insecure-requests");
header("Strict-Transport-Security: max-age=31536000; includeSubDomains");
header("X-Content-Type-Options: nosniff");
header("Referrer-Policy: strict-origin-when-cross-origin");
header("Permissions-Policy: camera=(), microphone=(), geolocation=()");
header("X-Frame-Options: DENY");
Headers must be sent before the response body. Generate a cryptographically random nonce separately for every request.
Node.js / Express
app.use((req, res, next) => {
res.setHeader(
"Content-Security-Policy",
"default-src 'self'; base-uri 'self'; object-src 'none'; frame-ancestors 'none'; form-action 'self'; img-src 'self' data: https:; style-src 'self'; script-src 'self'; connect-src 'self'; upgrade-insecure-requests"
);
res.setHeader(
"Strict-Transport-Security",
"max-age=31536000; includeSubDomains"
);
res.setHeader("X-Content-Type-Options", "nosniff");
res.setHeader("Referrer-Policy", "strict-origin-when-cross-origin");
res.setHeader(
"Permissions-Policy",
"camera=(), microphone=(), geolocation=()"
);
res.setHeader("X-Frame-Options", "DENY");
res.removeHeader("X-Powered-By");
next();
});
A site using CDNs, WebSockets, OAuth, payments or maps will need a broader but still explicit CSP.
A rollout process that avoids outages
Inventory
List all script, style, font and image origins; API and WebSocket endpoints; iframes; OAuth/payment popups; CDN resources; user uploads; HSTS subdomains and browser capabilities used by the application.
Observe
- run CSP in Report-Only;
- collect reports and browser console violations;
- test every critical user path;
- include mobile and supported browsers;
- inspect error pages, redirects and static assets.
Enforce gradually
- begin with
object-src,base-uri,frame-ancestorsandform-action; - move to static resources;
- enforce strict
script-srclast; - increase HSTS
max-agegradually; - test COOP/COEP separately against popup and cross-origin integrations.
Monitor regressions
CI tests should fetch representative URLs, detect duplicate or conflicting headers, confirm MIME types, detect accidental removal of CSP/HSTS and run end-to-end login and payment flows.
How to test headers
Check the response directly:
curl -I https://example.com/
Follow redirects:
curl -IL https://example.com/
Inspect a specific resource:
curl -I https://example.com/assets/app.js
Verify headers on the homepage, login, 404 and 500 responses; confirm that CSP is not duplicated with conflicting policies; make sure HSTS is sent only over HTTPS; check MIME types; and verify that a CDN or reverse proxy has not stripped the headers.
The free POLPROG Security Headers Inspector checks HSTS, CSP, framing protection and other controls. [21] Use the DNS & SSL Inspector for certificate and DNS checks, and Website Health Check for a broader SEO, performance, accessibility and security review.
Deployment checklist
CSP
- Restrictive
default-src. -
object-src 'none'. - Limited
base-uri. -
frame-ancestorsmatches the real embedding model. -
form-actionrestricts form destinations. - Inline code uses a nonce or hash when required.
- The nonce is unique per response.
- No unjustified
'unsafe-inline'. - No unjustified
'unsafe-eval'. - Policy tested in Report-Only first.
- Report endpoint has rate limits and limited retention.
HTTPS and HSTS
- Every page and resource works over HTTPS.
- HTTP redirects directly to HTTPS.
- Certificates are monitored.
- All subdomains are inventoried.
-
max-ageincreased in stages. -
includeSubDomainsis safe. -
preloadadded only after reviewing consequences.
Other controls
-
X-Content-Type-Options: nosniff. - Correct
Content-Typeon every response. - Explicit
Referrer-Policy. -
Permissions-Policydisables unused capabilities. - No
ALLOW-FROMin X-Frame-Options. - COOP does not break OAuth or payments.
- COEP does not block required third-party assets.
- CORP matches each resource’s sharing model.
- Session cookies use
Secure,HttpOnlyand appropriateSameSite. - Sensitive responses use correct
Cache-Control. - Technology version headers are removed.
-
X-XSS-Protectionis absent or disabled. -
Expect-CT, HPKP and oldReport-Toare removed.
Verdict
For most business websites and web applications, the right order is:
- Correct HTTPS, MIME types and secure cookies.
- An application-specific CSP rolled out through Report-Only.
- HSTS deployed in stages.
nosniff, Referrer-Policy, Permissions-Policy and framing protection.- COOP, COEP and CORP only when the cross-origin model is understood.
- Removal of obsolete and information-leaking headers.
The best security-header set is not the longest one. It is the smallest policy that accurately matches the application, survives critical-path testing and remains monitored after every deployment.

