The Problem
Content Security Policy (CSP) headers tell the browser which sources are allowed to execute scripts, load styles, and make requests. A strict CSP blocks inline scripts (<script> tags), inline styles (style="..."), and eval() calls.
Symptoms
- Console error: "Refused to execute inline script because it violates CSP"
- JavaScript code does not run
- Analytics, chat widgets, or third-party scripts fail to load
- Page renders without interactive features
- Works locally but not in production
Real Error Message
Refused to execute inline script because it violates the following
Content Security Policy directive: "script-src 'self'". Either the
'unsafe-inline' keyword, a hash ('sha256-...'), or a nonce
('nonce-...') is required to enable inline execution.Common Causes
Cause 1: Inline Script Tags
<!-- BLOCKED by CSP -->
<script>
window.dataLayer = window.dataLayer || [];
gtag('config', 'GA-XXXX');
</script>Cause 2: Inline Event Handlers
<!-- BLOCKED by CSP -->
<button onclick="handleSubmit()">Submit</button>Cause 3: Third-Party Scripts
<!-- BLOCKED by CSP if not in script-src -->
<script src="https://www.googletagmanager.com/gtag/js?id=GA-XXXX"></script>How to Fix It
Fix 1: Use Nonces for Inline Scripts
<!-- Generate nonce server-side -->
<script nonce="abc123-random">
window.dataLayer = window.dataLayer || [];
</script>Content-Security-Policy: script-src 'self' 'nonce-abc123-random' https://www.googletagmanager.comFix 2: Use Hashes for Static Inline Scripts
# Generate SHA-256 hash of the inline script
echo -n "window.dataLayer = [];" | openssl dgst -sha256 -binary | openssl base64 -A
# Output: sha256-abc123...Content-Security-Policy: script-src 'self' 'sha256-abc123...'Fix 3: Move Inline Scripts to External Files
// analytics.js
window.dataLayer = window.dataLayer || [];
gtag('config', 'GA-XXXX');<script src="/analytics.js"></script>Fix 4: Replace Inline Event Handlers
// Instead of onclick="handleSubmit()"
document.getElementById('submit-btn').addEventListener('click', handleSubmit);Fix 5: Proper CSP Header Configuration
# nginx.conf
add_header Content-Security-Policy "
default-src 'self';
script-src 'self' https://www.googletagmanager.com https://cdn.example.com;
style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;
img-src 'self' data: https:;
font-src 'self' https://fonts.gstatic.com;
connect-src 'self' https://api.example.com;
frame-ancestors 'none';
" always;Fix 6: Report-Only Mode for Testing
# Test CSP before enforcing
add_header Content-Security-Policy-Report-Only "
default-src 'self';
script-src 'self';
report-uri /csp-report;
" always;