How can we help you?

Content Security Policy (CSP) and Velaro

The Velaro live chat widget is fully compatible with Content Security Policy (CSP). No unsafe-eval or unsafe-inline is required for scripts.

The simplest approach: allow by domain

Add Velaro's CDN to your script-src directive:

`` Content-Security-Policy: script-src 'self' https://app-cdn.velaro.com; ``

This works for all server platforms and requires no per-request server logic.

Nonce-based CSP (most secure)

A nonce is a cryptographically random one-time token generated on your server for each page request. You add it to your CSP header and to the Velaro embed script tag — the browser only runs scripts with a matching nonce.

Because the Velaro embed script uses strict-dynamic, the nonce only needs to go on the one embed tag. All scripts the embed loads (shim.js, frame.js) are trusted automatically through the chain — no need to list CDN domains in script-src.

Step 1: Generate a nonce on your server

The nonce must be regenerated on every request. Never cache or hardcode it.

ASP.NET Core:

``csharp var nonce = Convert.ToBase64String(RandomNumberGenerator.GetBytes(32)); HttpContext.Items["csp-nonce"] = nonce; Response.Headers.Append("Content-Security-Policy", $"script-src 'nonce-{nonce}' 'strict-dynamic'; connect-src https://.velaro.com wss://.velaro.com https://app-cdn.velaro.com https://*.service.signalr.net;"); ``

Node.js / Express:

``javascript const crypto = require('crypto'); app.use((req, res, next) => { res.locals.nonce = crypto.randomBytes(32).toString('base64'); res.setHeader('Content-Security-Policy', script-src 'nonce-${res.locals.nonce}' 'strict-dynamic'; connect-src https://.velaro.com wss://.velaro.com https://app-cdn.velaro.com;); next(); }); ``

Next.js (middleware):

``javascript import { NextResponse } from 'next/server'; import crypto from 'crypto'; export function middleware(request) { const nonce = Buffer.from(crypto.randomUUID()).toString('base64'); const response = NextResponse.next(); response.headers.set('Content-Security-Policy', script-src 'nonce-${nonce}' 'strict-dynamic'; connect-src https://.velaro.com wss://.velaro.com https://app-cdn.velaro.com;); response.headers.set('x-nonce', nonce); return response; } ``

PHP:

``php $nonce = base64_encode(random_bytes(32)); header("Content-Security-Policy: script-src 'nonce-$nonce' 'strict-dynamic'; connect-src https://.velaro.com wss://.velaro.com https://app-cdn.velaro.com;"); ``

Python / Django:

``python import secrets, base64 nonce = base64.b64encode(secrets.token_bytes(32)).decode() response['Content-Security-Policy'] = f"script-src 'nonce-{nonce}' 'strict-dynamic'; connect-src https://.velaro.com wss://.velaro.com https://app-cdn.velaro.com;" ``

Ruby on Rails:

``ruby nonce = SecureRandom.base64(32) response.headers['Content-Security-Policy'] = "script-src 'nonce-#{nonce}' 'strict-dynamic'; connect-src https://.velaro.com wss://.velaro.com https://app-cdn.velaro.com;" ``

Java / Spring:

``java String nonce = Base64.getEncoder().encodeToString(new SecureRandom().generateSeed(32)); response.setHeader("Content-Security-Policy", "script-src 'nonce-" + nonce + "' 'strict-dynamic'; connect-src https://.velaro.com wss://.velaro.com https://app-cdn.velaro.com;"); ``

Cloudflare Workers:

``javascript const nonce = btoa(String.fromCharCode(...crypto.getRandomValues(new Uint8Array(32)))); response.headers.set('Content-Security-Policy', script-src 'nonce-${nonce}' 'strict-dynamic'; connect-src https://.velaro.com wss://.velaro.com https://app-cdn.velaro.com;); ``

Step 2: Add the nonce to the Velaro embed script tag

``html ``

Replace YOUR_NONCE_HERE with the nonce your server generates per request.

Full CSP directive reference

DirectiveRequired valuesPurpose
script-src'nonce-<value>' 'strict-dynamic'Authorizes the Velaro embed script and all scripts it loads dynamically
connect-srchttps://.velaro.comAPI calls (visitor, engagement, main API)
wss://.velaro.comReal-time WebSocket connections (SignalR)
https://app-cdn.velaro.comCDN asset loading
https://.service.signalr.netAzure SignalR service connections
style-src'unsafe-inline'Widget applies inline styles for positioning and theming
https://fonts.googleapis.comGoogle Fonts stylesheets (if widget fonts are configured)
font-srchttps://fonts.gstatic.comGoogle Font files
img-src data:Agent headshots, company logos, inline SVG icons
frame-src'self' blob:Widget renders inside an iframe for isolation

Optional directives by feature:

FeatureDirectiveValue
Chat notification soundsmedia-srchttps://app-cdn.velaro.com
Calendly schedulingframe-srchttps://calendly.com
Video ChatPermissions-Policycamera=(self), microphone=(self)

CSP hash alternative (static pages only)

If your page content never changes, you can hash the inline script instead of using a nonce:

  1. Copy the exact content of the Velaro inline script block.
  2. Generate a SHA-256 hash: echo -n "script content" | openssl dgst -sha256 -binary | base64
  3. Add to your header: script-src 'self' 'sha256-HASH_VALUE' 'strict-dynamic';

This only works for inline scripts. For external scripts loaded via src=, use domain allowlisting or nonces.

Hosted platforms (WordPress, Webflow, Squarespace)

WordPress: Use a CSP plugin such as "CSP Nonce" or "HTTP Headers" that handles nonce generation automatically. Configure the plugin to apply nonces to inline scripts on all pages where Velaro is embedded.

Webflow / Squarespace / Wix: These platforms do not support server-side nonce generation. Use a Cloudflare Worker as a reverse proxy to inject CSP headers and nonces into responses. If CSP is not a hard requirement, the domain allowlist approach (script-src 'self' https://app-cdn.velaro.com) works without server-side changes.

Permissions Policy

Restrict browser features the Velaro widget does not use:

`` Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=() ``

If Velaro Video Chat is enabled, allow camera and microphone for the origin:

`` Permissions-Policy: camera=(self), microphone=(self), geolocation=(), payment=() ``

Monitoring CSP violations

Before enforcing a CSP in production, use Content-Security-Policy-Report-Only to collect violations without blocking anything:

`` Content-Security-Policy-Report-Only: script-src 'nonce-' 'strict-dynamic'; connect-src https://.velaro.com wss://.velaro.com https://app-cdn.velaro.com; report-uri https://your-reporting-endpoint/csp ``

Switch to Content-Security-Policy once the report shows no violations.

Testing your configuration

  1. Open the page with the Velaro widget.
  2. Open browser DevTools (F12) → Console tab.
  3. Look for velaro:initializing and velaro:initialized — widget loaded successfully.
  4. No red CSP errors in the console = policy is correct.

Troubleshooting

SymptomLikely causeFix
Widget does not load, console shows "Refused to execute script"Missing script-src entryAdd https://app-cdn.velaro.com or nonce to script-src
"Refused to load script from...shim..."Nonce missing or mismatched on embed tagVerify the nonce= attribute is set on the embed script and matches the CSP header exactly
Widget loads but chat messages failMissing connect-srcAdd https://.velaro.com wss://.velaro.com to connect-src
Avatar or images brokenMissing img-srcAdd https://app-cdn.velaro.com to img-src
Nonce mismatch on every loadNonce is cached or staticNonce must be regenerated on every request — never cache it
Fonts brokenMissing font-srcAdd https://fonts.gstatic.com to font-src
Widget frame blockedMissing frame-srcAdd blob: to frame-src
"Refused to evaluate a string as JavaScript"Old version of Velaro widgetunsafe-eval is not needed — contact Velaro support to confirm you are on the current widget version

Contact Velaro support with your full CSP header value and the browser console error messages if issues persist.

Share: Email

Was this article helpful?