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.

Here's a strict CSP policy that works with Focus UI:

CSP 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 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:

CSP Header
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:

CSP Header (Strictest)
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

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

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)

JavaScript
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

Next.js Config
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):

HTML
<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, or sandbox directives
  • 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:

CSP Header
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

  1. Open DevTools (F12)
  2. Go to the Console tab
  3. Look for CSP violation messages (they'll be highlighted in red)
  4. 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

⚠️ Third-Party Dependencies

Focus UI itself has zero runtime dependencies, but the documentation site uses:

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.

❌ Bad
<button onclick="doSomething()">Click</button>

<script>
  function doSomething() {
    FocusUI.Toast.show({ message: 'Hello!' })
  }
</script>
✅ Good
<button id="myButton">Click</button>

<script src="app.js"></script>
app.js
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.

CSP Header
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.

CSP Header
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:

CSP Header
Content-Security-Policy: script-src 'self' 'nonce-rAnD0m123';
HTML
<script src="https://cdn.focus-ui.de/v2.0.0/focus-ui.min.js" 
        nonce="rAnD0m123"></script>

The nonce must be:

Using Hashes

Alternatively, use SRI hashes in your CSP (requires knowing the exact file content):

CSP Header
Content-Security-Policy: script-src 'self' 'sha384-GDpJYcUtWAkXeEY/ue8/sOuXYpDByN+yjFe52qcCpTwGYyVze1NxcY1qeImkQNHH';

Best Practices

  1. Start with Report-Only - Test your policy before enforcing it
  2. Avoid 'unsafe-inline' - Focus UI doesn't need it, and it defeats the purpose of CSP
  3. Avoid 'unsafe-eval' - Allows eval(), which is dangerous
  4. Use HTTPS - CSP works best with HTTPS-only resources
  5. Self-host when possible - Reduces the need to whitelist external domains
  6. Pin to specific versions - Use /v2.0.0/ not /latest/
  7. Combine with SRI - Use both CSP and SRI for maximum security
  8. Test thoroughly - Check all pages and user flows

Additional Resources