Securing the Velaro Chat Widget with Content Security Policy (CSP)
The Velaro chat widget is fully compatible with strict Content Security Policy (CSP) using a per-request nonce and the CSP Level 3 'strict-dynamic' source expression. This is the modern, recommended way to lock down inline scripts on your website without maintaining a brittle allowlist of third-party domains.
No Velaro-side configuration is required. You control everything from your own response headers and HTML.
How It Works
When your page sends a CSP header containing script-src 'nonce-, browsers do two things:
- Only
tags whosenonceattribute matches the value in your header are allowed to execute. - Any script that was allowed to execute can dynamically create additional
elements (viadocument.createElement('script')) and those are implicitly trusted — the trust "propagates" throughstrict-dynamic.
The Velaro widget loader is a single tag on your page. As long as you put your nonce on that one tag, the widget loads under your CSP. The bundle the loader pulls in (jQuery, SignalR, React) is created via createElement('script') and inherits trust automatically. You do not need to know or specify any nonce for the sub-bundles.
Step-by-Step Setup
1. Generate a per-request nonce on your server
A nonce is a cryptographically random value, base64-encoded, regenerated for every HTTP response. Example helpers:
ASP.NET / C#: ``csharp var nonce = Convert.ToBase64String(RandomNumberGenerator.GetBytes(16)); HttpContext.Items["CspNonce"] = nonce; ``
Node.js / Express: ``javascript const crypto = require('crypto'); res.locals.nonce = crypto.randomBytes(16).toString('base64'); ``
PHP: ``php $nonce = base64_encode(random_bytes(16)); ``
2. Send a CSP header with the nonce and strict-dynamic
Set this response header on every HTML page that runs Velaro:
`` Content-Security-Policy: script-src 'nonce-``
Notes:
'unsafe-inline'andhttps:are ignored by browsers that understand'strict-dynamic'(Chrome 52+, Firefox 52+, Edge 79+, Safari 15.4+). They exist purely as a fallback for older browsers — required for graceful degradation per OWASP/Google guidance.object-src 'none'andbase-uri 'self'are recommended hardening; they don't affect Velaro.
3. Put the nonce on the Velaro loader script
This is the only line that needs the nonce:
``html ``
Replace with the value you generated in step 1. Replace SITE_ID with your Velaro Site ID.
4. Test
Open browser DevTools → Console. You should see no CSP violations related to the Velaro widget loading.
What You Do NOT Need to Do
- You do not need to nonce any other Velaro script tags. The widget bundle is loaded dynamically by the loader —
strict-dynamiccovers it. - You do not need to allowlist
cdn.velaro.cominscript-src. Withstrict-dynamic, source allowlists are ignored. - You do not need to ask Velaro to enable a flag. There is no Velaro-side configuration. The widget works the same regardless of whether your CSP includes a nonce.
Legacy Inline Widget — Important Caveat
If you embed the legacy inline widget (Velaro chat embedded directly into the page DOM instead of via the modern loader), two optional features use eval() and will be blocked by strict CSP:
- Custom Template Script field (configured in Velaro Admin → Chat Templates)
- Invite Timer Expressions (configured per chat invite)
If you use these features and want strict CSP, you have two choices:
- Add
'unsafe-eval'to yourscript-src. This weakens CSP but keeps the features working. - Migrate to the modern widget loader, which does not use those code paths.
The modern Velaro widget — the default for all new sites — does not use eval() and is fully compatible with strict CSP.
Verifying Your Setup
Use the CSP Evaluator from Google to score your CSP header. A passing score with 'strict-dynamic' looks like:
`` script-src 'nonce-...' 'strict-dynamic' ✓ Strong CSP object-src 'none' ✓ base-uri 'self' ✓ ``
Browser Support
| Browser | 'strict-dynamic' Support |
|---|---|
| Chrome / Edge | 52+ |
| Firefox | 52+ |
| Safari | 15.4+ |
| Older browsers | Falls back to source allowlist via https: |
Coverage is effectively universal for modern browser traffic.
Troubleshooting
The widget doesn't load and the console shows Refused to load the script ... Content Security Policy directive.
The nonce on the loader tag does not match the nonce in your CSP header. Check that both values are identical. The nonce should be generated server-side and injected into both the header and the script tag in the same response.
The widget loads but features stop working partway through.
Some legacy widget features use eval(). Either add 'unsafe-eval' or move to the modern widget. See the legacy widget caveat above.
My CSP report endpoint is flooded with violations from the Velaro domain.
If strict-dynamic is in your CSP, source allowlists are ignored — but report-only CSPs without strict-dynamic will flag every script the widget injects. Make sure your enforcing CSP header includes 'strict-dynamic', not just your report-only one.
Questions
Contact your Velaro account representative or open a support ticket if you need help validating your CSP configuration.
Was this article helpful?