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.
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.
1. 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. 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.
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.
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. Reporting-Endpoints replaces the deprecated Report-To header and should be preferred for current deployments.
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.
2. 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.
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. 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. Removal may take weeks because the list is shipped inside browsers.
3. 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.
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.
4. 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.
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.
5. 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>.
For compatibility, add:
X-Frame-Options: DENY
or:
X-Frame-Options: SAMEORIGIN
MDN recommends frame-ancestors for modern deployments because it is more expressive. 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.
6. 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.
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. Identify the capabilities the application uses, then explicitly disable relevant unused ones.
7. 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.
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.
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.
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.
8. 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.
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.
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.
9. 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.
10. 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. 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.
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.
11. 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.
12. 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.
13. 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. 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.

