Content Security Policy (CSP) Headers – Complete Reference Guide

Web security isn’t optional anymore. If you’re running a website in 2025, you absolutely need Content Security Policy headers configured. CSP is your first line of defense against cross-site scripting (XSS) attacks, code injection, and a host of other threats that can compromise your site and your users’ data.

This guide walks you through everything you need to know about CSP: what it is, why you need it, every directive available, and how to implement it correctly on your server.

What Is Content Security Policy?

Content Security Policy (CSP) is an HTTP response header that tells browsers exactly which resources they’re allowed to load on your web page. Think of it as a whitelist for your website—you explicitly define where scripts, styles, images, fonts, and other resources can come from.

When a browser receives a page with a CSP header, it checks every resource against your policy before loading it. If something doesn’t match your rules, the browser blocks it and logs a violation. This prevents attackers from injecting malicious code into your site, even if they find a vulnerability in your application.

CSP was introduced in 2004 and became a W3C recommendation in 2012. Today, all modern browsers support CSP Level 2, and CSP Level 3 features are widely adopted. It’s not bleeding-edge tech, it’s a proven security standard you should’ve implemented yesterday.

Why You Need CSP Headers

Cross-site scripting remains one of the most common and dangerous web vulnerabilities. According to security research, nearly 20% of cyberattacks use XSS or similar injection techniques. Without CSP, your site is vulnerable.

Here’s what CSP protects against:

Cross-Site Scripting (XSS): The primary threat CSP was designed to combat. If an attacker manages to inject a <script> tag into your page, CSP blocks it from executing unless it comes from an approved source.

Data Injection Attacks: Prevents unauthorized code from being loaded into your pages through compromised third-party resources or injection points.

Clickjacking: Using the frame-ancestors directive, you can control which sites can embed your pages in iframes, preventing clickjacking attacks.

Mixed Content: Force all resources to load over HTTPS, preventing man-in-the-middle attacks on HTTP resources.

Compromised CDNs: If a CDN you use gets compromised, CSP limits what malicious scripts can do since they won’t have the necessary permissions.

Beyond security, CSP is increasingly required for compliance with security frameworks like ISO 27001. Not having a CSP can be a compliance risk.

Complete CSP Directives Reference

CSP policies are built from directives. Each directive controls a specific type of resource or behavior. Multiple directives are separated by semicolons, and each directive has a name followed by allowed source values.

Fetch Directives

Fetch directives control where resources can be loaded from. These are the workhorses of your CSP configuration.

default-src: The fallback for all other fetch directives. If you don’t specify a directive like script-src, the browser uses default-src. Set this first.

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

script-src: Controls JavaScript sources. This is critical—most XSS attacks rely on injecting scripts. Be strict here.

Content-Security-Policy: script-src 'self' https://cdn.example.com;

style-src: Controls CSS sources. Similar to scripts, but for stylesheets.

Content-Security-Policy: style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;

img-src: Controls image sources. You’ll often need to allow data: for inline images and https: for third-party images.

Content-Security-Policy: img-src 'self' data: https:;

font-src: Controls font sources. Essential if you’re using web fonts from Google Fonts or similar services.

Content-Security-Policy: font-src 'self' https://fonts.gstatic.com;

connect-src: Controls XMLHttpRequest, WebSocket, fetch(), and EventSource connections. Any AJAX calls must match these sources.

Content-Security-Policy: connect-src 'self' https://api.example.com;

media-src: Controls <audio> and <video> sources.

Content-Security-Policy: media-src 'self' https://media.example.com;

object-src: Controls <object>, <embed>, and <applet> elements. Set this to 'none' unless you have a specific need for plugins.

Content-Security-Policy: object-src 'none';

frame-src: Controls nested browsing contexts loaded via <frame> and <iframe>.

Content-Security-Policy: frame-src 'self' https://www.youtube.com;

worker-src: Controls Worker, SharedWorker, and ServiceWorker sources.

Content-Security-Policy: worker-src 'self';

manifest-src: Controls application manifest sources.

Content-Security-Policy: manifest-src 'self';

child-src: Deprecated in favor of frame-src and worker-src, but still supported. Controls nested browsing contexts and workers.

prefetch-src: Controls resources that can be prefetched or prerendered.

fenced-frame-src: Controls sources for <fencedframe> elements (newer privacy-focused feature).

Document Directives

These directives control properties of the document itself.

base-uri: Restricts URLs that can be used in a document’s <base> element. Prevents attackers from changing the base URL for relative links.

Content-Security-Policy: base-uri 'self';

sandbox: Enables a sandbox for the requested resource, similar to the iframe sandbox attribute. You can allow specific features like allow-forms or allow-scripts.

Content-Security-Policy: sandbox allow-forms allow-scripts;

Navigation Directives

Control where documents can navigate.

form-action: Restricts URLs that can be used as form action targets. Prevents form submissions to malicious sites.

Content-Security-Policy: form-action 'self';

frame-ancestors: Controls which parent frames can embed the current page. This replaces the older X-Frame-Options header.

Content-Security-Policy: frame-ancestors 'self' https://trusted-partner.com;

navigate-to: Restricts URLs to which a document can navigate (removed from CSP Level 3).

Reporting Directives

Configure how CSP violations are reported.

report-uri: Deprecated but still widely supported. Specifies a URL where the browser sends violation reports.

Content-Security-Policy: report-uri /csp-violation-report;

report-to: Modern replacement for report-uri. References a reporting endpoint defined in the Reporting-Endpoints header.

Content-Security-Policy: report-to csp-endpoint;

Other Directives

upgrade-insecure-requests: Automatically upgrades HTTP requests to HTTPS. Essential for forcing secure connections.

Content-Security-Policy: upgrade-insecure-requests;

require-trusted-types-for: Requires Trusted Types API for DOM XSS sinks. Advanced protection for modern applications.

Content-Security-Policy: require-trusted-types-for 'script';

trusted-types: Defines allowed Trusted Types policies.

Content-Security-Policy: trusted-types default;

Note: block-all-mixed-content: is deprecated. Blocks loading any HTTP resources on HTTPS pages. Now handled by default in modern browsers.

Source Values Reference

Directives accept various source values that define what’s allowed.

  • ‘none’ – Blocks all sources for this directive. Use single quotes.
  • ‘self’ – Allows resources from the same origin (protocol, domain, and port).
  • ‘unsafe-inline’ – Allows inline scripts and styles. Avoid this—it defeats much of CSP’s purpose. Use nonces or hashes instead.
  • ‘unsafe-eval’ – Allows eval() and similar text-to-code functions. Avoid unless absolutely necessary.
  • ‘strict-dynamic’ – Trusts scripts loaded by already-trusted scripts. Powerful for modern applications using nonces.
  • ‘nonce-[random]’ – Allows scripts/styles with a matching nonce attribute. Generate a new random nonce for each request.
<script nonce="rAnd0m">...</script>
  • ‘sha256-[hash]’ – Allows scripts/styles matching a specific SHA-256 hash. Calculate the hash of your script content.
  • https: – Allows any HTTPS source.
  • http://example.com – Allows a specific domain. Can include protocol, wildcards for subdomains (*.example.com), and ports.
  • data: – Allows data: URIs. Common for images.
  • blob: – Allows blob: URIs.
  • filesystem: – Allows filesystem: URIs.

Implementing CSP Headers

CSP headers can be set at the server level, in application code, or via HTML meta tags (with limitations). Server-level configuration is most common and reliable.

Apache Configuration

For Apache servers, you’ll need the mod_headers module enabled. On Ubuntu/Debian systems:

sudo a2enmod headers
sudo systemctl restart apache2

Add CSP headers in your virtual host configuration or .htaccess file:

In httpd.conf or virtual host config:

<VirtualHost *:443>
    ServerName www.example.com

    # Basic CSP
    Header set Content-Security-Policy "default-src 'self'; script-src 'self' https://cdn.example.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' https://fonts.gstatic.com; connect-src 'self'; frame-ancestors 'self'; base-uri 'self'; form-action 'self';"

    # Other configuration...
</VirtualHost>

In .htaccess file:

<IfModule mod_headers.c>
    # Start in report-only mode for testing
    Header set Content-Security-Policy-Report-Only "default-src 'self'; script-src 'self' https://cdn.example.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' https://fonts.gstatic.com; connect-src 'self'; frame-ancestors 'self'; base-uri 'self'; form-action 'self'; report-uri /csp-report;"

    # When ready, switch to enforcing mode
    # Header set Content-Security-Policy "default-src 'self'; script-src 'self' https://cdn.example.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' https://fonts.gstatic.com; connect-src 'self'; frame-ancestors 'self'; base-uri 'self'; form-action 'self';"
</IfModule>

Restart Apache after making changes:

sudo systemctl restart apache2

NGINX Configuration

For nginx, add the CSP header in your server block. Edit your site configuration file (typically in /etc/nginx/sites-available/):

server {
    listen 443 ssl http2;
    server_name www.example.com;

    # Basic CSP header
    add_header Content-Security-Policy "default-src 'self'; script-src 'self' https://cdn.example.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' https://fonts.gstatic.com; connect-src 'self'; frame-ancestors 'self'; base-uri 'self'; form-action 'self';" always;

    # For testing, use report-only mode
    # add_header Content-Security-Policy-Report-Only "default-src 'self'; script-src 'self' https://cdn.example.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' https://fonts.gstatic.com; connect-src 'self'; frame-ancestors 'self'; base-uri 'self'; form-action 'self'; report-uri /csp-report;" always;

    # Other configuration...
}

The always parameter ensures the header is sent regardless of response code (200, 404, 500, etc.).

Reload nginx after making changes:

sudo systemctl reload nginx

Using Nonces with NGINX

For better security without 'unsafe-inline', implement nonces. This requires generating a random value for each request:

server {
    # Generate a random nonce for each request
    set_secure_random_alphanum $cspNonce 32;

    # Replace placeholder in HTML with actual nonce
    sub_filter_once off;
    sub_filter 'CSP_NONCE_PLACEHOLDER' $cspNonce;

    # Add CSP header with nonce
    add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'nonce-$cspNonce'; style-src 'self' 'nonce-$cspNonce'; img-src 'self' data: https:; font-src 'self' https://fonts.gstatic.com; connect-src 'self';" always;
}

In your HTML, use the placeholder:

<script nonce="CSP_NONCE_PLACEHOLDER">
    // Your inline script
</script>

HTML Meta Tag

You can set CSP via meta tag, though this method doesn’t support all features (like report-uri or frame-ancestors):

<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' https://cdn.example.com;">

Use this only for static sites or when you can’t modify server configuration. Server-level headers are always preferable.

Testing and Validation

Before enforcing a CSP in production, test it thoroughly. Breaking your site with an overly restrictive policy is worse than having no policy at all.

Report-Only Mode

Start with Content-Security-Policy-Report-Only instead of Content-Security-Policy. This logs violations without blocking content, letting you identify issues:

Header set Content-Security-Policy-Report-Only "default-src 'self'; script-src 'self'; report-uri /csp-report;"

Set up an endpoint to receive violation reports. The browser sends JSON reports like this:

{
  "csp-report": {
    "document-uri": "https://example.com/page",
    "violated-directive": "script-src 'self'",
    "blocked-uri": "https://malicious.com/script.js",
    "source-file": "https://example.com/page",
    "line-number": 23
  }
}

Monitor these reports for a few weeks, adjusting your policy as needed before switching to enforcement mode.

Browser Developer Tools

All modern browsers show CSP violations in the console:

Refused to load the script 'https://untrusted.com/script.js' because it violates the following Content Security Policy directive: "script-src 'self'".

Open your browser’s developer tools (F12) and check the Console tab while browsing your site. Fix any violations before going live.

Online Testing Tools

Several tools help validate and test your CSP:

CSP Evaluator (Google): Analyzes your policy for common misconfigurations and bypass techniques. Identifies weak directives and suggests improvements.

Report URI Analyzer: Parses and validates CSP headers, checking syntax and identifying potential issues.

SecurityHeaders.com: Scans your site for security headers including CSP and provides a grade.

Mozilla Observatory: Comprehensive security scanner that includes CSP checks.

CSP Hash Generator: Calculate SHA-256 hashes for inline scripts and styles.

Testing Checklist

Before deploying CSP to production:

  1. Deploy in report-only mode for at least 2-4 weeks
  2. Review violation reports daily initially, then weekly
  3. Test all user flows: login, checkout, form submissions, etc.
  4. Check on multiple browsers (Chrome, Firefox, Safari, Edge)
  5. Verify third-party integrations still work (analytics, chat widgets, payment processors)
  6. Test on mobile browsers
  7. Review reports one final time before switching to enforcement mode
  8. Keep monitoring violations after going live

Best Practices and Common Patterns

Start Strict, Then Loosen

Begin with the most restrictive policy possible:

Content-Security-Policy: default-src 'none';

This blocks everything. Then add only what you need:

Content-Security-Policy: default-src 'none'; script-src 'self'; style-src 'self'; img-src 'self'; font-src 'self'; connect-src 'self';

This approach ensures you don’t accidentally allow more than necessary.

Avoid ‘unsafe-inline’ and ‘unsafe-eval’

These directives defeat most of CSP’s XSS protection. Instead:

  • Use nonces for inline scripts and styles
  • Use hashes for small inline snippets
  • Move inline code to external files
  • Refactor code that uses eval()

If you must use 'unsafe-inline' temporarily, plan to remove it.

Use ‘strict-dynamic’ for Modern Sites

If you’re using nonces, add 'strict-dynamic' to allow scripts loaded by trusted scripts:

Content-Security-Policy: script-src 'nonce-random123' 'strict-dynamic';

This lets your nonce-approved scripts load other scripts dynamically, which is common in modern JavaScript applications.

Don’t Forget frame-ancestors

Replace the deprecated X-Frame-Options header with the frame-ancestors directive:

Content-Security-Policy: frame-ancestors 'self';

This prevents clickjacking by controlling which sites can embed your pages.

Combine with Other Security Headers

CSP works best alongside other security headers:

# Prevent MIME type sniffing
Header set X-Content-Type-Options "nosniff"

# Strict Transport Security (force HTTPS)
Header set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"

# Disable old XSS protection (CSP handles this now)
Header set X-XSS-Protection "0"

# Referrer policy
Header set Referrer-Policy "strict-origin-when-cross-origin"

Production-Ready Policy Example

Here’s a solid starting point for most sites:

Content-Security-Policy: 
  default-src 'self';
  script-src 'self' 'nonce-{random}' https://cdn.example.com;
  style-src 'self' 'nonce-{random}' https://fonts.googleapis.com;
  img-src 'self' data: https:;
  font-src 'self' https://fonts.gstatic.com;
  connect-src 'self' https://api.example.com;
  media-src 'self';
  object-src 'none';
  frame-src 'self' https://www.youtube.com;
  frame-ancestors 'self';
  base-uri 'self';
  form-action 'self';
  upgrade-insecure-requests;

Replace {random} with a unique nonce generated per request.

Common Issues and Troubleshooting

Scripts Not Loading: Check browser console for CSP violations. Add the blocked source to script-src or use nonces/hashes.

Inline Styles Blocked: Either add 'unsafe-inline' (not recommended) or use nonces/hashes, or move styles to external files.

Google Analytics Broken: Add Google’s domains to your policy:

script-src 'self' https://www.google-analytics.com https://ssl.google-analytics.com;
connect-src 'self' https://www.google-analytics.com;

Forms Not Submitting: Ensure form-action includes your form targets:

form-action 'self' https://payment-processor.com;

Third-Party Widgets Failing: Most widgets need multiple directives. Check their documentation or examine violation reports to identify required sources.

Moving Forward

Content Security Policy isn’t a set-it-and-forget-it solution. As your site evolves, your CSP needs to evolve with it. New third-party integrations, changed CDNs, or added features all require CSP updates.

Start with report-only mode, monitor violations, and gradually tighten your policy. The goal is maximum security without breaking functionality. It takes time to get it right, but the protection CSP provides is worth the effort.

Your site’s security depends on multiple layers, and CSP is one of the most effective layers you can implement. Combined with proper input validation, output encoding, and secure coding practices, CSP makes it significantly harder for attackers to exploit your site.

Don’t wait for a breach to take security seriously. Implement CSP headers today.


Related Resources:

Share this Article
Carrie Smaha
Carrie Smaha Senior Manager Marketing Operations

Carrie enjoys working on demand generation and product marketing projects that tap into multi-touch campaign design, technical SEO, content marketing, software design, and business operations.

More Articles by Carrie