Content Security Policy (CSP) and Velaro
What is CSP and why does it matter?
A Content Security Policy is an HTTP response header that tells the browser which scripts are allowed to run on your page. If your site uses CSP, you need to authorize the Velaro embed script or it will be blocked. This guide shows you exactly how to do that.
The complete, always-current list of Velaro hosts to allow (a short wildcard policy and an explicit per-host list, for both Velaro Chat and Velaro Messaging) is in Content Security Policy for Velaro Chat and Velaro Messaging. Use that article for the host list; use this one for nonces, hashes and troubleshooting.
Two ways to allow Velaro
Pick the path that matches what your security system supports. Both keep chat working. Path A needs the least upkeep.
Path A (recommended, simplest): allow the Velaro wildcard
Allow https://.velaro.com and wss://.velaro.com in each of these directives: connect-src, script-src, frame-src, img-src and style-src.
Content-Security-Policy:
script-src 'self' https://*.velaro.com https://eastprodcdn.azureedge.net;
connect-src 'self' https://*.velaro.com wss://*.velaro.com https://*.service.signalr.net wss://*.service.signalr.net;
frame-src 'self' https://*.velaro.com;
img-src 'self' data: https://*.velaro.com https://eastprodcdn.azureedge.net https://galleryuseastprod.blob.core.windows.net https://velaromessagingprod.blob.core.windows.net;
style-src 'self' https://*.velaro.com;
Why the wildcard: Velaro runs in more than one region and uses regional and failover hostnames. Traffic can move to a different host during maintenance or a regional failover. With the wildcard in place, chat keeps working through that move and you change nothing on your side. A policy that lists fixed hosts can block the new host, and chat then fails even though Velaro is healthy. The wildcard only covers subdomains of velaro.com, which Velaro controls.
Path B: your security system does not allow wildcard entries
If your security system does not allow wildcard entries, add each host below by exact name. Spell them exactly as shown and keep every one in your policy.
| Host | Add to |
|---|---|
https://eastprodcdn.azureedge.net |
script-src, img-src |
https://cdn-us-east.velaro.com |
script-src, style-src, img-src |
https://api-visitor-us-east.velaro.com |
connect-src |
https://api-engagement-us-east.velaro.com |
connect-src |
https://api-main-us-east.velaro.com |
connect-src |
https://api-messaging-us-east.velaro.com |
connect-src |
https://signalr-engagement-us-east.velaro.com and wss://signalr-engagement-us-east.velaro.com |
connect-src |
https://signalr-engagement-app-prd.velaro.com and wss://signalr-engagement-app-prd.velaro.com |
connect-src |
https://livefluence-asrs-eastus.velaro.com and wss://livefluence-asrs-eastus.velaro.com |
connect-src |
https://messaging.velaro.com |
frame-src, connect-src (Velaro Messaging) |
https://workspace.velaro.com |
frame-src, connect-src (Velaro Messaging) |
https://galleryuseastprod.blob.core.windows.net and https://velaromessagingprod.blob.core.windows.net |
img-src |
https://.service.signalr.net and wss://.service.signalr.net |
connect-src |
Allow both SignalR hosts. On the current chat stack, the visitor's browser first calls signalr-engagement-us-east.velaro.com. It is then redirected to livefluence-asrs-eastus.velaro.com for the negotiate call and the websocket connection. A policy that allows only the first host blocks the second one, and chat stops connecting. A customer ran into exactly this, so list both.
The full, current list for Velaro Chat and Velaro Messaging is also in Content Security Policy for Velaro Chat and Velaro Messaging. Prefer a nonce-based policy? See the nonce section below and Securing the Velaro Messaging Widget with Content Security Policy (CSP).
The simplest approach: allow by domain
Add Velaro's CDN to your script-src directive:
Content-Security-Policy: script-src 'self' https://*.velaro.com https://eastprodcdn.azureedge.net;
This is the easiest option and works for all server platforms. Add Velaro's API domain to connect-src as well:
Content-Security-Policy:
script-src 'self' https://*.velaro.com https://eastprodcdn.azureedge.net;
connect-src 'self' https://*.velaro.com wss://*.velaro.com https://*.service.signalr.net wss://*.service.signalr.net;
img-src 'self' data: https://*.velaro.com https://eastprodcdn.azureedge.net https://galleryuseastprod.blob.core.windows.net https://velaromessagingprod.blob.core.windows.net;
Nonce-based CSP ('strict-dynamic')
A nonce is a random one-time token generated on your server for each page request. You add it to your CSP header and to the Velaro script tag. With 'strict-dynamic', the browser trusts scripts that are dynamically loaded by nonce-authorized scripts.
Important: The Velaro shim (shim.js) loads the chat widget (frame.js) inside an about:blank iframe. That iframe inherits your page's CSP. Because strict-dynamic propagation does not cross document boundaries, you need to both add a nonce to the shim script tag and include https://*.velaro.com https://eastprodcdn.azureedge.net in your script-src. Velaro's shim forwards the nonce automatically — this covers the iframe load. Adding the CDN domain is belt-and-suspenders to ensure compatibility across all browsers.
Also allow the Velaro hosts in connect-src, frame-src, img-src and media-src. A nonce only covers scripts. Without the connect-src entries the widget loads but chat messages do not arrive. See the first policy above, or article 772.
Step 1: Generate a nonce on your server
The nonce must be cryptographically random and different for every request.
ASP.NET Core:
var nonce = Convert.ToBase64String(RandomNumberGenerator.GetBytes(16));
HttpContext.Items["csp-nonce"] = nonce;
Response.Headers.Append("Content-Security-Policy",
$"script-src 'self' 'nonce-{nonce}' 'strict-dynamic' https://*.velaro.com https://eastprodcdn.azureedge.net;");
Node.js / Express:
const crypto = require('crypto');
app.use((req, res, next) => {
res.locals.nonce = crypto.randomBytes(16).toString('base64');
res.setHeader('Content-Security-Policy',
`script-src 'self' 'nonce-${res.locals.nonce}' 'strict-dynamic' https://*.velaro.com https://eastprodcdn.azureedge.net;`);
next();
});
Next.js (middleware):
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 'self' 'nonce-${nonce}' 'strict-dynamic' https://*.velaro.com https://eastprodcdn.azureedge.net;`);
response.headers.set('x-nonce', nonce);
return response;
}
PHP:
$nonce = base64_encode(random_bytes(16));
header("Content-Security-Policy: script-src 'self' 'nonce-$nonce' 'strict-dynamic' https://*.velaro.com https://eastprodcdn.azureedge.net;");
Python / Django:
import secrets, base64
nonce = base64.b64encode(secrets.token_bytes(16)).decode()
response['Content-Security-Policy'] = (
f"script-src 'self' 'nonce-{nonce}' 'strict-dynamic' https://*.velaro.com https://eastprodcdn.azureedge.net;"
)
Ruby on Rails:
nonce = SecureRandom.base64(16)
response.headers['Content-Security-Policy'] =
"script-src 'self' 'nonce-#{nonce}' 'strict-dynamic' https://*.velaro.com https://eastprodcdn.azureedge.net;"
Java / Spring:
String nonce = Base64.getEncoder().encodeToString(new SecureRandom().generateSeed(16));
response.setHeader("Content-Security-Policy",
"script-src 'self' 'nonce-" + nonce + "' 'strict-dynamic' https://*.velaro.com https://eastprodcdn.azureedge.net;");
Cloudflare Workers:
const nonce = btoa(crypto.getRandomValues(new Uint8Array(16)).toString());
response.headers.set('Content-Security-Policy',
`script-src 'self' 'nonce-${nonce}' 'strict-dynamic' https://*.velaro.com https://eastprodcdn.azureedge.net;`);
Step 2: Add the nonce to the Velaro script tag
<script nonce="YOUR_NONCE_HERE" src="https://eastprodcdn.azureedge.net/shim.js"
data-deployment-id="YOUR_DEPLOYMENT_ID">
</script>
Velaro's shim reads the nonce from its own <script> tag and forwards it to the frame bundle it loads — you only need to set it once on the outer tag.
CSP hash alternative (static pages only)
If your page never changes, you can hash the script tag instead of using a nonce:
1. Take the exact content of your inline script block.
2. Generate a SHA-256 hash: echo -n "script content" | openssl dgst -sha256 -binary | base64
3. Add it to your header: script-src 'self' 'sha256-HASH_VALUE' https://*.velaro.com https://eastprodcdn.azureedge.net;
This only works for inline scripts. For external scripts loaded from a src=, use domain allowlisting or nonces.
Google Fonts with CSP
If you use Google Fonts alongside Velaro, add these to your CSP:
Content-Security-Policy:
script-src 'self' https://*.velaro.com https://eastprodcdn.azureedge.net;
style-src 'self' https://fonts.googleapis.com;
font-src 'self' https://fonts.gstatic.com;
connect-src 'self' https://*.velaro.com wss://*.velaro.com https://*.service.signalr.net wss://*.service.signalr.net;
img-src 'self' data: https://*.velaro.com https://eastprodcdn.azureedge.net https://galleryuseastprod.blob.core.windows.net https://velaromessagingprod.blob.core.windows.net;
frame-src 'self' https://*.velaro.com https://eastprodcdn.azureedge.net;
media-src 'self' https://*.velaro.com https://galleryuseastprod.blob.core.windows.net https://velaromessagingprod.blob.core.windows.net;
Subresource Integrity (SRI)
SRI lets the browser verify that the Velaro script has not been tampered with in transit. Add the integrity and crossorigin attributes to the script tag:
<script
src="https://eastprodcdn.azureedge.net/shim.js"
integrity="sha256-HASH_FROM_VELARO"
crossorigin="anonymous">
</script>
Contact Velaro support for the current integrity hash when you need it.
Permissions Policy
Restrict browser features the Velaro widget does not need. Paste this header alongside your CSP:
Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=()
If you use Velaro Video Chat, allow camera and microphone for your own origin:
Permissions-Policy: camera=(self), microphone=(self), geolocation=(), payment=()
Troubleshooting CSP issues
| Symptom | Likely cause | Fix |
|---|---|---|
| Widget does not load, console shows "Refused to execute script" | Missing script-src entry |
Add https://*.velaro.com https://eastprodcdn.azureedge.net or nonce to script-src |
| Widget loads but chat messages fail | Missing connect-src |
Add https://.velaro.com wss://.velaro.com https://.service.signalr.net wss://.service.signalr.net |
| Avatar or images broken | Missing img-src |
Add https://*.velaro.com https://eastprodcdn.azureedge.net to img-src |
| Nonce mismatch | Nonce is cached or static | Nonce MUST be regenerated on every request — never cache it |
| Fonts broken | Missing font-src |
Add https://fonts.gstatic.com to font-src |
| Widget blocked on nonce-only CSP | strict-dynamic doesn't cross iframe boundary |
Add https://*.velaro.com https://eastprodcdn.azureedge.net to script-src alongside the nonce |
| Chat works from some regions or at some times but is blocked at others | Policy lists individual Velaro hosts and Velaro served the widget from a different regional host | Use the https://.velaro.com and wss://.velaro.com wildcard so failover between regions is never blocked |
You do NOT need 'unsafe-eval' or 'unsafe-inline' for Velaro.
Was this article helpful?