Content Security Policy (CSP)
Configure Content Security Policy headers to protect your application from XSS attacks while using Focus UI.
What is CSP?
Content Security Policy (CSP) is an HTTP header that helps prevent Cross-Site Scripting (XSS) and other code injection attacks by controlling which resources the browser is allowed to load.
Focus UI is designed to work with strict CSP policies - no inline scripts, no eval(), and no unsafe-inline requirements.
Recommended CSP for Focus UI
Here's a strict CSP policy that works with Focus UI:
Content-Security-Policy:
default-src 'self';
script-src 'self' https://cdn.focus-ui.de;
style-src 'self' https://cdn.focus-ui.de;
font-src 'self' https://cdnjs.cloudflare.com https://fonts.gstatic.com;
img-src 'self' data: https:;
connect-src 'self';
frame-ancestors 'none';
base-uri 'self';
form-action 'self';
Directive Breakdown
| Directive | Value | Why? |
|---|---|---|
default-src |
'self' |
Only load resources from your own domain by default |
script-src |
'self' https://cdn.focus-ui.de |
Allow scripts from your domain and Focus UI CDN |
style-src |
'self' https://cdn.focus-ui.de |
Allow stylesheets from your domain and Focus UI CDN |
font-src |
'self' https://cdnjs.cloudflare.com https://fonts.gstatic.com |
Allow fonts from your domain and icon libraries (Font Awesome, Google Fonts) |
img-src |
'self' data: https: |
Allow images from your domain, data URIs, and HTTPS sources |
connect-src |
'self' |
Only allow AJAX/fetch requests to your own domain |
frame-ancestors |
'none' |
Prevent your site from being embedded in iframes (clickjacking protection) |
base-uri |
'self' |
Prevent base tag injection |
form-action |
'self' |
Only allow forms to submit to your own domain |
CSP for Self-Hosted Focus UI
If you're self-hosting Focus UI, your CSP can be even stricter:
Content-Security-Policy:
default-src 'self';
script-src 'self';
style-src 'self';
font-src 'self' https://cdnjs.cloudflare.com https://fonts.gstatic.com;
img-src 'self' data: https:;
connect-src 'self';
frame-ancestors 'none';
base-uri 'self';
form-action 'self';
Or if you're also self-hosting Font Awesome and don't use external fonts:
Content-Security-Policy:
default-src 'self';
script-src 'self';
style-src 'self';
font-src 'self';
img-src 'self' data:;
connect-src 'self';
frame-ancestors 'none';
base-uri 'self';
form-action 'self';
Implementation Methods
1. HTTP Header (Recommended)
Configure your web server to send the CSP header:
Nginx
add_header Content-Security-Policy "default-src 'self'; script-src 'self' https://cdn.focus-ui.de; style-src 'self' https://cdn.focus-ui.de; font-src 'self' https://cdnjs.cloudflare.com; img-src 'self' data: https:; connect-src 'self'; frame-ancestors 'none'; base-uri 'self'; form-action 'self';" always;
Apache
Header set Content-Security-Policy "default-src 'self'; script-src 'self' https://cdn.focus-ui.de; style-src 'self' https://cdn.focus-ui.de; font-src 'self' https://cdnjs.cloudflare.com; img-src 'self' data: https:; connect-src 'self'; frame-ancestors 'none'; base-uri 'self'; form-action 'self';"
Express.js (Node.js)
const express = require('express')
const helmet = require('helmet')
const app = express()
app.use(helmet.contentSecurityPolicy({
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "https://cdn.focus-ui.de"],
styleSrc: ["'self'", "https://cdn.focus-ui.de"],
fontSrc: ["'self'", "https://cdnjs.cloudflare.com", "https://fonts.gstatic.com"],
imgSrc: ["'self'", "data:", "https:"],
connectSrc: ["'self'"],
frameAncestors: ["'none'"],
baseUri: ["'self'"],
formAction: ["'self'"]
}
}))
Next.js
module.exports = {
async headers() {
return [
{
source: '/:path*',
headers: [
{
key: 'Content-Security-Policy',
value: "default-src 'self'; script-src 'self' https://cdn.focus-ui.de; style-src 'self' https://cdn.focus-ui.de; font-src 'self' https://cdnjs.cloudflare.com; img-src 'self' data: https:; connect-src 'self'; frame-ancestors 'none'; base-uri 'self'; form-action 'self';"
}
]
}
]
}
}
2. Meta Tag (Fallback)
If you can't configure HTTP headers, you can use a meta tag (less secure, but better than nothing):
<meta http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self' https://cdn.focus-ui.de; style-src 'self' https://cdn.focus-ui.de; font-src 'self' https://cdnjs.cloudflare.com; img-src 'self' data: https:; connect-src 'self';">
Meta Tag Limitations
The meta tag approach has limitations:
- Cannot use
frame-ancestors,report-uri, orsandboxdirectives - Can be removed or modified by XSS attacks (if they happen before the meta tag)
- HTTP header is always preferred for security
Testing Your CSP
Report-Only Mode
Before enforcing CSP, test it in report-only mode to find violations without breaking your site:
Content-Security-Policy-Report-Only: default-src 'self'; script-src 'self' https://cdn.focus-ui.de; ...
Check the browser console for CSP violation reports. Once you've fixed all violations, switch to the enforcing header.
Browser Developer Tools
- Open DevTools (F12)
- Go to the Console tab
- Look for CSP violation messages (they'll be highlighted in red)
- Fix each violation by updating your CSP policy or removing the violating code
Online CSP Evaluator
Use Google's CSP Evaluator to check if your policy is secure.
Focus UI CSP Compatibility
✅ What Focus UI Does Right
- No inline scripts - All JavaScript is in external files
- No inline styles - All CSS is in external stylesheets or CSS classes
- No eval() - No dynamic code evaluation
- No unsafe-inline - Doesn't require 'unsafe-inline' for scripts or styles
- No unsafe-eval - Doesn't require 'unsafe-eval'
- Nonce-friendly - Compatible with CSP nonces if needed
⚠️ Third-Party Dependencies
Focus UI itself has zero runtime dependencies, but the documentation site uses:
- Font Awesome (optional) - Requires
font-src https://cdnjs.cloudflare.com - Google Fonts (optional) - Requires
font-src https://fonts.gstatic.comandstyle-src https://fonts.googleapis.com
These are not required for Focus UI itself - only if you choose to use them in your application.
Common CSP Issues
Issue: "Refused to execute inline script"
Cause: You have inline <script> tags in your HTML.
Solution: Move all JavaScript to external files or use a nonce/hash.
<button onclick="doSomething()">Click</button>
<script>
function doSomething() {
FocusUI.Toast.show({ message: 'Hello!' })
}
</script>
<button id="myButton">Click</button>
<script src="app.js"></script>
document.getElementById('myButton').addEventListener('click', () => {
FocusUI.Toast.show({ message: 'Hello!' })
})
Issue: "Refused to load stylesheet"
Cause: Your CSP doesn't allow the stylesheet's origin.
Solution: Add the CDN to style-src.
Content-Security-Policy: ... style-src 'self' https://cdn.focus-ui.de; ...
Issue: "Refused to load font"
Cause: Font Awesome or other icon fonts are blocked.
Solution: Add the font CDN to font-src.
Content-Security-Policy: ... font-src 'self' https://cdnjs.cloudflare.com; ...
Advanced CSP Features
Using Nonces
For maximum security, use nonces (cryptographically random strings) instead of allowing entire CDN domains:
Content-Security-Policy: script-src 'self' 'nonce-rAnD0m123';
<script src="https://cdn.focus-ui.de/v2.0.0/focus-ui.min.js"
nonce="rAnD0m123"></script>
The nonce must be:
- Generated randomly for each page load
- At least 128 bits of entropy
- Base64 encoded
- Sent in both the CSP header and the script tag
Using Hashes
Alternatively, use SRI hashes in your CSP (requires knowing the exact file content):
Content-Security-Policy: script-src 'self' 'sha384-GDpJYcUtWAkXeEY/ue8/sOuXYpDByN+yjFe52qcCpTwGYyVze1NxcY1qeImkQNHH';
Best Practices
- Start with Report-Only - Test your policy before enforcing it
- Avoid 'unsafe-inline' - Focus UI doesn't need it, and it defeats the purpose of CSP
- Avoid 'unsafe-eval' - Allows eval(), which is dangerous
- Use HTTPS - CSP works best with HTTPS-only resources
- Self-host when possible - Reduces the need to whitelist external domains
- Pin to specific versions - Use
/v2.0.0/not/latest/ - Combine with SRI - Use both CSP and SRI for maximum security
- Test thoroughly - Check all pages and user flows