Dev Tools

OWASP Security Headers: Complete Compliance Guide for Web Apps

OWASP's security header requirements cover CSP, HSTS, Permissions Policy, and more. This guide walks through full compliance implementation.

๐Ÿ“… May 25, 2026ยทโฑ๏ธ 11 min readยทโœ๏ธ Cikal Studio Labs
๐Ÿ›๏ธ

OWASP and the Security Headers Standard

The Open Web Application Security Project (OWASP) produces the most authoritative and widely-referenced guidance on web application security worldwide. The OWASP Top 10 โ€” a consensus list of the most critical web application security risks โ€” consistently includes security misconfiguration as one of the top categories, which directly encompasses missing and incorrectly configured security headers. The OWASP Secure Headers Project provides a dedicated, detailed specification for every HTTP security header that web applications should implement.

Unlike ad hoc security checklists, OWASP guidance is developed through community consensus involving thousands of security professionals, reviewed and updated based on real-world threat data, and referenced by regulatory frameworks and compliance auditors worldwide. Following the OWASP Secure Headers Project is not just security best practice โ€” it's increasingly a compliance requirement in regulated industries.

The OWASP Secure Headers Project Priority List

OWASP assigns implementation priority to security headers based on their impact and adoption complexity. Here is the complete list in priority order:

  • Priority 1 โ€” Strict-Transport-Security (HSTS): Prevents protocol downgrade attacks and cookie hijacking. Single highest-impact header for transport security. Required configuration: max-age=31536000; includeSubDomains minimum, with preload for maximum protection.
  • Priority 2 โ€” Content-Security-Policy (CSP): The most comprehensive XSS defense available at the browser level. Requires application-specific whitelist configuration โ€” the most complex header to implement correctly, but the most powerful protection against code injection.
  • Priority 3 โ€” X-Frame-Options: Clickjacking prevention. Simple to implement, zero breaking-change risk for correctly configured applications. Use DENY unless your application intentionally allows iframe embedding by same-origin pages.
  • Priority 4 โ€” X-Content-Type-Options: MIME sniffing prevention. Set to nosniff. One-line implementation with no side effects for properly served content.
  • Priority 5 โ€” Referrer-Policy: Controls information leakage through HTTP Referrer headers. Recommended value: strict-origin-when-cross-origin for most applications.
  • Priority 6 โ€” Permissions-Policy: Limits browser feature exposure. Disable every feature your application doesn't actively use โ€” camera, microphone, geolocation, payment, USB, Bluetooth, interest cohort.
  • Priority 7 โ€” Cross-Origin-Opener-Policy (COOP): Isolates your browsing context from cross-origin windows. Required for enabling SharedArrayBuffer and high-precision timing. Recommended: same-origin.
  • Priority 8 โ€” Cross-Origin-Resource-Policy (CORP): Prevents cross-origin reading of your resources. Protects against cross-site Spectre-class data leakage attacks. Set to same-origin or same-site depending on your CDN architecture.
  • Priority 9 โ€” Cross-Origin-Embedder-Policy (COEP): Requires all sub-resources to explicitly opt in to cross-origin loading. Required alongside COOP to achieve cross-origin isolation status in the browser.

CSP Directive Reference: The Complete Whitelist

Content Security Policy's power comes from its granularity โ€” different fetch directives control different types of resources. Understanding each directive enables precise configuration that provides maximum security without breaking legitimate functionality:

  • default-src: The catch-all fallback for all fetch directives not explicitly specified. Setting default-src 'self' restricts everything to same-origin by default, then you can selectively relax specific resource types.
  • script-src: Controls JavaScript sources. The most security-critical directive. Avoid 'unsafe-inline' โ€” use nonces or hashes for inline scripts instead. Avoid 'unsafe-eval' โ€” eliminate eval() and Function() from your codebase.
  • style-src: Controls CSS sources. 'unsafe-inline' is unfortunately required for many CSS frameworks โ€” nonces can be used to allow specific inline styles without allowing all inline styles.
  • img-src: Controls image sources. Often needs data: for inline base64 images and https: for user-generated content from arbitrary hosts.
  • connect-src: Controls fetch(), XHR, WebSocket, and EventSource destinations. This is your data exfiltration prevention โ€” restrict it tightly to only the API endpoints your application actually calls.
  • font-src: Controls web font sources. If you use Google Fonts, you need https://fonts.gstatic.com here.
  • frame-src: Controls which origins can be loaded in iframes your page creates. Separate from frame-ancestors, which controls who can embed your page.
  • frame-ancestors: Replaces X-Frame-Options. 'none' prevents all embedding; 'self' allows same-origin embedding only.
  • base-uri: Restricts which URLs can appear in the <base> element. Set to 'self' to prevent base tag injection attacks that redirect all relative URLs.
  • form-action: Restricts where forms can submit. Prevents form hijacking by injected content.
  • object-src: Controls plugins (Flash, Java applets). Set to 'none' in 2026 โ€” no modern application should use plugin-based content.
  • worker-src: Controls Web Worker and ServiceWorker script sources. Restrict to 'self' unless you load workers from external CDNs.
  • manifest-src: Controls Web App Manifest sources.
  • upgrade-insecure-requests: Instructs browsers to upgrade HTTP requests to HTTPS automatically. Useful during HTTPS migration to fix mixed content warnings.

CSP Nonce Implementation Pattern

Using nonces is the secure alternative to 'unsafe-inline' that allows specific inline scripts without opening the door to arbitrary inline script injection. Each page request generates a cryptographically random nonce value, which is included in the CSP header and in the nonce attribute of approved inline scripts:

In the CSP header: script-src 'self' 'nonce-{RANDOM_VALUE}'

In the HTML: <script nonce="{RANDOM_VALUE}">// approved inline script</script>

The nonce must be cryptographically random (at least 128 bits), unique per request, and never reused. Attackers cannot inject script tags with a valid nonce because they cannot predict the next nonce value. This approach maintains the XSS protection of strict CSP while accommodating legitimate inline scripts that can't be externalized.

HSTS Preloading: Eligibility and Submission Process

Browser HSTS preloading provides the strongest possible protection โ€” browsers refuse to even make an initial HTTP connection to your domain, eliminating the first-visit attack window that even correctly implemented HSTS can't protect against. The eligibility requirements for inclusion in the Chrome preload list (which Firefox, Edge, and Safari also incorporate):

  • Valid HTTPS must be served from the root domain (apex domain, not just www subdomain)
  • The HSTS header must be present on the HTTPS response with max-age of at least 31536000 (one year)
  • The includeSubDomains directive must be present โ€” all subdomains must be HTTPS
  • The preload directive must be present โ€” an explicit declaration of intent to be preloaded
  • Submit at hstspreload.org โ€” the site validates your configuration automatically before accepting the submission

After submission, inclusion takes 1-3 months to propagate into stable browser releases. Plan accordingly if your HTTPS migration timeline is constrained.

Compliance Scoring Methodologies

Several security header grading tools provide compliance scores useful for security benchmarking and audit reporting. SecurityHeaders.com provides an A+ through F letter grade based on header presence and basic configuration correctness. OWASP's own scoring approach weights headers by security impact: HSTS and CSP contribute the most to the score (25-30 points each), with X-Frame-Options, X-Content-Type-Options, Referrer-Policy, and Permissions-Policy contributing the remainder. A score of 70+ indicates basic compliance; 90+ indicates hardened configuration.

๐Ÿ’ก Compliance auditing tip: Run your headers through multiple analysis tools โ€” different tools flag different issues. A header that passes one grader may still have exploitable misconfiguration that another catches. Use SecurityHeaders.com, Mozilla Observatory, and a dedicated security header analyzer for comprehensive assessment before submitting compliance evidence.

Implementing Headers Across Different Frameworks

The correct implementation method varies by server technology:

  • Next.js (next.config.ts): Use the headers() async function returning header arrays with source patterns. Apply a base security header set to all routes with source: '/(.*)' and override specific routes as needed.
  • Express.js: Use Helmet middleware (npm install helmet). app.use(helmet()) enables sensible defaults for all standard security headers. Configure individual headers through Helmet's options object for customization.
  • Nginx: Add add_header Header-Name "value" always; directives to your server or location blocks. The always keyword ensures headers are added to error responses as well as success responses.
  • Apache: Use Header always set Header-Name "value" in httpd.conf, .htaccess, or VirtualHost configuration. Ensure mod_headers is enabled (a2enmod headers on Debian/Ubuntu systems).
  • Cloudflare Workers / Edge: Modify headers in the Worker response object before returning. Excellent for applying security headers consistently across an entire origin without modifying the origin server configuration.