How can we help you?

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.

Share: Email

Was this article helpful?