Web Security

Common Security Header Misconfigurations and How to Avoid Them

Learn about the most common security header mistakes, real-world examples of misconfigurations, and practical guidance for avoiding them.

By Inventive HQ Team

Common Security Header Mistakes That Weaken Your Protection

The most damaging security header misconfigurations are not missing headers but headers that look present while doing nothing: a Content-Security-Policy that allows 'unsafe-inline', a CORS policy that reflects any origin alongside credentials, an HSTS header with a max-age of seconds, a deprecated X-Frame-Options: ALLOW-FROM, an over-broad Permissions-Policy, and headers applied to only some responses. Each passes a casual glance yet leaves the underlying vulnerability wide open, creating false confidence in protection that does not actually exist.

That is the summary an AI overview can give you. What it cannot give you is a ranked, fix-in-hand view of which mistakes cost you the most and the exact directive that closes each gap. This post covers the misconfigurations that matter, ordered by real-world impact, then walks each one in detail. It pairs with our companion explainer on what HTTP security headers are — read that first if you need the fundamentals; read this to avoid getting them wrong.

The Misconfigurations That Matter, Ranked

#MisconfigurationSeverityWhat goes wrongThe fix
1Reflected/wildcard CORS with credentialsCriticalAccess-Control-Allow-Origin echoes any origin while Allow-Credentials: true lets any site read logged-in responsesValidate Origin against an allowlist; never reflect it blindly; never pair * with credentials
2'unsafe-inline' / 'unsafe-eval' in CSP script-srcCriticalInjected inline scripts execute — CSP stops blocking XSS, its main jobUse nonces ('nonce-…') or hashes ('sha256-…'); remove unsafe-*
3Missing HSTS or short max-ageHighmax-age=300 is forgotten in minutes, reopening the HTTPS-downgrade / MITM windowmax-age=31536000; includeSubDomains; add preload only after months of testing
4X-Frame-Options: ALLOW-FROMHighIgnored by all modern browsers → zero clickjacking protectionDENY/SAMEORIGIN + CSP frame-ancestors for the modern, multi-origin case
5Over-permissive or absent Permissions-PolicyMediumCamera, mic, geolocation, and payment APIs stay available to any embedded scriptDeny what you don't use: camera=(), microphone=(), geolocation=()
6Header on only some responsesMediumErrors, redirects, and static assets ship unprotected while the home page passes an auditApply headers globally with always (Apache) / edge middleware; verify on 404/500
7Wildcard or unsafe CSP default-src *HighAllows every origin — equivalent to no CSPStart at default-src 'self', add specific trusted origins only
8Syntax errors / typos (defualt-src)MediumMalformed headers are silently ignored — false confidenceAutomated validation in CI; scan the live response
Scanning HTTP response headers for misconfigurations A response-header panel where a scan line sweeps down, marking a short-max-age HSTS, a wildcard CSP, and a reflected CORS header as misconfigured, and a SAMEORIGIN frame policy and a strict referrer policy as correct. HTTP Response Headers scanning… ! Strict-Transport-Security: max-age=300
<rect x="24" y="118" width="592" height="34" rx="6" fill="#ffffff" stroke="#fde68a"/>
<circle cx="46" cy="135" r="9" fill="#f59e0b"><animate attributeName="opacity" values="1;0.35;1" dur="1.6s" begin="0.3s" repeatCount="indefinite"/></circle>
<text x="42" y="139" fill="#ffffff" font-size="13" font-weight="700">!</text>
<text x="66" y="139" font-family="ui-monospace, monospace">Content-Security-Policy: default-src *</text>

<rect x="24" y="160" width="592" height="34" rx="6" fill="#ffffff" stroke="#fde68a"/>
<circle cx="46" cy="177" r="9" fill="#f59e0b"><animate attributeName="opacity" values="1;0.35;1" dur="1.6s" begin="0.6s" repeatCount="indefinite"/></circle>
<text x="42" y="181" fill="#ffffff" font-size="13" font-weight="700">!</text>
<text x="66" y="181" font-family="ui-monospace, monospace">Access-Control-Allow-Origin: (reflected) + credentials</text>

<rect x="24" y="202" width="592" height="34" rx="6" fill="#ffffff" stroke="#bbf7d0"/>
<circle cx="46" cy="219" r="9" fill="#16a34a"/>
<path d="M42 219 l3 3 l6 -7" fill="none" stroke="#ffffff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<text x="66" y="223" font-family="ui-monospace, monospace">X-Frame-Options: SAMEORIGIN</text>

<rect x="24" y="244" width="592" height="34" rx="6" fill="#ffffff" stroke="#bbf7d0"/>
<circle cx="46" cy="261" r="9" fill="#16a34a"/>
<path d="M42 261 l3 3 l6 -7" fill="none" stroke="#ffffff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<text x="66" y="265" font-family="ui-monospace, monospace">Referrer-Policy: strict-origin-when-cross-origin</text>

Amber = looks present, protects nothing. Green = configured correctly.

Even well-intentioned organizations implement security headers incorrectly, creating false confidence in security that doesn't actually exist. Understanding common misconfiguration patterns helps you avoid the most dangerous mistakes. These errors range from trivial formatting issues to fundamental misunderstandings of what headers actually protect against.

Mistake #1: Overly Permissive Content Security Policy

The most common CSP mistake is making it so permissive that it provides no actual protection. Examples of dangerously weak CSP configurations:

Content-Security-Policy: default-src *

This allows scripts and resources from anywhere, defeating the entire purpose of CSP. It's equivalent to having no CSP at all.

Content-Security-Policy: script-src 'unsafe-inline' *

Allowing unsafe-inline makes CSP ineffective against inline script injection attacks, which are among the most common XSS vectors.

Content-Security-Policy: default-src 'self' *

While less obviously broken than the above, allowing * alongside 'self' negates any protection benefit.

How to avoid it: Start with default-src 'self' and explicitly allow only necessary resources from specific trusted origins. Never use wildcards in CSP unless absolutely necessary, and never combine them with unsafe directives. Use report-only mode to test CSP before deployment so you understand the impact.

If you need to allow inline scripts (which is generally discouraged), use nonces or hashes rather than 'unsafe-inline':

Content-Security-Policy: script-src 'self' 'nonce-abc123def456'

Mistake #1b: Wildcard CORS Combined With Credentials

This is the single highest-impact header mistake, because it hands authenticated data to any website. It hides behind a browser rule that developers work around the wrong way.

You cannot combine a wildcard origin with credentials — the browser refuses to expose the response:

Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true

Because that combination is blocked, developers commonly reflect the incoming Origin header straight back to satisfy the browser:

# Attacker sends: Origin: https://evil.example
# Server echoes:
Access-Control-Allow-Origin: https://evil.example
Access-Control-Allow-Credentials: true

This is functionally a credentialed wildcard. Any origin the victim's browser sends — including an attacker's site — is approved, so a logged-in victim visiting evil.example lets that page read their authenticated API responses (profile data, tokens, account details). This is a Cross-Site Request Forgery-adjacent data-exfiltration bug and one of the most frequently exploited CORS misconfigurations.

How to avoid it: Maintain an explicit allowlist of trusted origins. Compare the request's Origin against that list and only echo it back when it matches. Never reflect arbitrary origins, and never send Access-Control-Allow-Credentials: true for public, unauthenticated endpoints. See our CORS glossary entry for the full request/response flow.

Mistake #2: Missing SameSite Attributes on Cookies

Cookies without proper SameSite attributes remain vulnerable to Cross-Site Request Forgery (CSRF) attacks:

Set-Cookie: sessionid=abc123

This cookie is sent with cross-site requests, allowing attackers to forge requests on behalf of users.

How to avoid it: Always specify a SameSite attribute on sensitive cookies:

Set-Cookie: sessionid=abc123; SameSite=Strict

Use SameSite=Strict for session and authentication cookies. If you need cookies to work across some cross-site contexts, use SameSite=Lax as a minimum.

Advertisement

Mistake #3: HSTS Configuration Errors

Several HSTS mistakes are common:

Forgetting includeSubDomains:

Strict-Transport-Security: max-age=31536000

This protects only the main domain, leaving subdomains vulnerable to MITM attacks.

Setting max-age too short:

Strict-Transport-Security: max-age=300; includeSubDomains

A 5-minute expiration means browsers frequently discard the HSTS policy, reducing protection. The standard is 1 year (31536000 seconds) or longer.

Implementing HSTS before fully committing to HTTPS:

HSTS causes browsers to reject all HTTP connections. If you implement it before ensuring all resources load over HTTPS, users will experience broken pages.

How to avoid it: Use max-age=31536000; includeSubDomains as your standard HSTS configuration. Only implement HSTS after confirming all resources (including third-party content) load over HTTPS. Consider preload list submission only after running HSTS successfully for several months.

Mistake #4: X-Frame-Options Set to ALLOW-FROM

Some developers use an outdated X-Frame-Options configuration:

X-Frame-Options: ALLOW-FROM https://example.com

The ALLOW-FROM directive is deprecated and not supported in modern browsers. Don't rely on it for security.

How to avoid it: Use only the supported values:

  • DENY - Prevent framing completely
  • SAMEORIGIN - Allow framing only by same-origin pages
  • (omit the header or don't use ALLOW-FROM)

For anything more nuanced than "same origin only" — such as allowing a specific set of partner domains to frame you — use the CSP frame-ancestors directive, which is the modern replacement and supports multiple origins. See X-Frame-Options vs. CSP frame-ancestors for how the two interact and which wins.

Mistake #5: Inconsistent Header Implementation Across Environments

Different environments (development, staging, production) sometimes have different security header configurations:

# Production
Strict-Transport-Security: max-age=31536000; includeSubDomains

# Development
# (HSTS not configured)

This creates a disconnect where development doesn't match production, meaning issues aren't caught until deployment.

How to avoid it: Use identical security header configurations across all environments. If specific configurations are needed for development (like allowing unsafe-inline for development tools), use environment-specific configuration management rather than manual differences. This ensures consistent testing.

Mistake #6: Referrer-Policy Too Permissive

Setting overly permissive referrer policies leaks user data:

Referrer-Policy: unsafe-url

This sends the full URL as referrer to all destinations, potentially including sensitive information in query parameters.

How to avoid it: Use privacy-respecting policies:

Referrer-Policy: strict-origin-when-cross-origin

This balances functionality (same-origin requests get full URLs) with privacy (cross-origin requests get only the origin).

Mistake #7: Neglecting to Update Headers When Third-Party Resources Change

A common pattern: implement strict CSP, then add a third-party service later without updating CSP:

# Original CSP
Content-Security-Policy: default-src 'self'

# Application adds analytics...
# (CSP is never updated)

The application breaks because the analytics script violates CSP, and you either remove CSP or use overly permissive configurations to work around it.

How to avoid it: Implement a process where adding third-party resources requires updating relevant headers. Document every CSP exception with the reason and which third-party service necessitates it. Monitor CSP violation reports to catch unexpected changes. Our guide on handling CSP for third-party resources covers nonces, hashes, and strict-dynamic for exactly this problem.

Mistake #8: Misunderstanding Permissions-Policy Scope

Permissions-Policy is sometimes thought to provide complete API protection:

Permissions-Policy: camera=()

This prevents the site from using camera through the camera API, but it doesn't prevent web-based screen recording or other indirect access to video capture.

How to avoid it: Understand that Permissions-Policy controls specific browser APIs, not all possible data access. Combine it with other security controls like CSP (to prevent scripts that might try to abuse APIs) and CORS headers (to prevent cross-origin API requests).

Mistake #9: Public Reporting of CSP Violations

Some developers expose CSP violation reports publicly:

Content-Security-Policy: default-src 'self'; report-uri /public/csp-violations

CSP reports can reveal application architecture and third-party services you use, providing reconnaissance information to attackers.

How to avoid it: Report CSP violations to an endpoint that requires authentication or is not publicly accessible. Monitor these reports internally rather than exposing them:

Content-Security-Policy: default-src 'self'; report-uri /admin/csp-violations

Mistake #10: Implementing Headers Without Testing Across Browsers

Headers sometimes work in some browsers but not others:

X-Content-Type-Options: nosniff

Works in modern browsers but might interact unexpectedly with older browsers in particular environments.

How to avoid it: Test security headers across target browsers. Use tools like caniuse.com to verify browser support. Test not just that headers are sent, but that they have the intended security effect in each browser you support.

Mistake #11: Not Accounting for Browser Parsing Differences

CSP and other headers with complex syntax can be parsed differently across browsers:

Content-Security-Policy: script-src 'self' https: 'unsafe-inline'

Different browsers interpret https: differently (some allow any HTTPS source, others require specific domains). The header's behavior might vary across browsers.

How to avoid it: Use explicit domain whitelists rather than protocol wildcards:

Content-Security-Policy: script-src 'self' cdn.trusted.com

Test in each target browser to ensure behavior matches your expectations.

Mistake #12: Setting Wrong Directives for Your Use Case

Some organizations implement headers that don't match their threat model:

X-Frame-Options: DENY

If you legitimately need to iframe your application for partner integration, DENY breaks functionality. SAMEORIGIN would be more appropriate.

How to avoid it: Before implementing each header, understand what threats it addresses and whether those threats apply to your application. For example, if you don't process payments, you might prioritize different headers than a payment processor would.

Mistake #13: Forgetting to Apply Headers to Error Responses

Security headers sometimes only appear on successful responses:

If 404 or 500 error pages don't include security headers, attackers gain information about your application's behavior without the same protection.

How to avoid it: Configure your web server to apply security headers to all responses, including error pages. Use the always directive in Apache or equivalents in other servers:

Header always set X-Content-Type-Options "nosniff"

Mistake #14: Not Combining Security Headers with Other Controls

Implementing security headers but neglecting other protections:

Content-Security-Policy: default-src 'self'
# (No HTTPS enforcement, no authentication, weak dependencies)

CSP alone doesn't protect against all attack vectors. It should be one layer in defense-in-depth.

How to avoid it: Implement security headers as part of a comprehensive strategy including:

  • HTTPS everywhere (enforced by HSTS)
  • Input validation and output encoding
  • Regular security updates for dependencies
  • Authentication and authorization controls
  • Security testing (SAST, DAST, penetration testing)

Mistake #15: Header Syntax Errors

Simple typos or formatting mistakes render headers ineffective:

Strict-Transport-Security max-age=31536000; includeSubDomains
# Missing colon after header name

Content-Security-Policy: defualt-src 'self'
# Typo in directive name (defualt vs default)

These malformed headers might be silently ignored by browsers, creating false confidence in security.

How to avoid it: Use automated validation tools to check header syntax. Security Header Analyzers and browser developer tools catch these issues. Use linting tools in your build process to validate headers before deployment.

Testing for Misconfigurations

Scan a live URL below to see exactly which headers ship, which are missing, and which are misconfigured:

Loading interactive tool...

Use these tools to catch misconfigurations:

Security Headers Analyzer - Scans your site and reports missing or misconfigured headers.

Mozilla Observatory - Provides detailed grades and recommendations for security headers.

Browser Developer Tools - Show what headers were actually sent and whether they're valid.

Automated Testing - Integrate header validation into your CI/CD pipeline to catch configuration changes before they reach production.

Avoiding Misconfiguration Through Process

Beyond technical controls, implement process improvements:

  1. Documentation - Document why each header is configured as it is
  2. Code Review - Review header changes just like you review code
  3. Testing - Test header functionality alongside application testing
  4. Monitoring - Monitor for CSP violations and other header-related issues
  5. Updates - Review headers periodically as new headers and directives are introduced

Conclusion: Effective Header Implementation Requires Attention to Detail

Security headers are powerful when correctly configured but can provide false confidence when misconfigured. Avoiding these common mistakes requires understanding not just what each header does, but why you're implementing it and what threats it actually protects against. Use automated tools to catch syntax errors and configuration issues, combine headers with other security controls, and maintain ongoing monitoring to ensure headers continue protecting your application as it evolves.

Frequently Asked Questions

What is the most dangerous security header misconfiguration?

Reflecting the request's Origin header into Access-Control-Allow-Origin while also sending Access-Control-Allow-Credentials: true is the highest-impact mistake, because it lets any website read a logged-in user's authenticated responses. A close second is allowing 'unsafe-inline' in the script-src directive of a Content-Security-Policy, which neutralizes CSP's protection against the most common cross-site scripting payloads.

Does a wildcard CORS policy work with credentials?

No. Browsers explicitly refuse to expose a credentialed response when Access-Control-Allow-Origin is the wildcard *. To work around that block, developers often echo the incoming Origin header back verbatim, which is functionally the same as a credentialed wildcard and exposes authenticated data to every origin. The fix is to validate the Origin against an allowlist and only reflect approved origins.

What HSTS max-age should I use?

Use max-age=31536000 (one year) with includeSubDomains as the standard. Short values such as max-age=300 cause browsers to forget the HTTPS-only policy within minutes, reopening a downgrade window. Only add the preload token after running HSTS with includeSubDomains successfully for several months, because preload is very hard to reverse.

Why is X-Frame-Options ALLOW-FROM a misconfiguration?

ALLOW-FROM was never widely supported and is ignored by every modern browser, so a header that relies on it provides no clickjacking protection at all. Use X-Frame-Options DENY or SAMEORIGIN for legacy coverage, and set a Content-Security-Policy frame-ancestors directive, which is the modern replacement and supports multiple allowed origins.

Do security headers need to be on error pages too?

Yes. If a header only appears on 200 responses but not on 404 or 500 pages, an attacker can operate against the unprotected error responses. In Apache use the always keyword (Header always set ...); in Nginx add_header applies to error responses when you include the always parameter on modern versions or set it in the relevant location blocks.

Is 'unsafe-inline' ever acceptable in a CSP?

Avoid it. 'unsafe-inline' allows any injected inline <script> to execute, which is exactly what CSP is meant to stop. If you cannot remove inline scripts immediately, use a per-request nonce ('nonce-...') or a hash ('sha256-...') instead, and treat 'unsafe-inline'/'unsafe-eval' as temporary debt to remove.

How do I test my security headers for misconfigurations?

Scan the live site with a headers analyzer, Mozilla Observatory, or your browser's network panel, and confirm each header is present, syntactically valid, and applied to every response including redirects and errors. Add the same checks to CI so a regression fails the build before it reaches production.

What is the difference between missing a header and misconfiguring it?

A missing header offers no protection but is easy to spot in a scan. A misconfigured header is more dangerous because it looks present and passes a superficial audit while providing little or no real protection, creating false confidence. A short-max-age HSTS, a wildcard CSP, and a typo'd directive name all fall into this category.

web-securityheadersconfigurationsecurity-mistakesbest-practices