How can we help?

Content Security Policy (CSP) and Velaro

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 simplest approach: allow by domain

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

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

This is the easiest option and works for all server platforms.

Nonce-based CSP (most secure)

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 script tag — only scripts with a matching nonce run.

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';");

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';`);
  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';`);
  response.headers.set('x-nonce', nonce); // pass to page
  return response;
}

PHP:

$nonce = base64_encode(random_bytes(16));
header("Content-Security-Policy: script-src 'self' 'nonce-$nonce' 'strict-dynamic';");

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';"

Ruby on Rails:

nonce = SecureRandom.base64(16)
response.headers['Content-Security-Policy'] =
  "script-src 'self' 'nonce-#{nonce}' 'strict-dynamic';"

Java / Spring:

String nonce = Base64.getEncoder().encodeToString(new SecureRandom().generateSeed(16));
response.setHeader("Content-Security-Policy",
    "script-src 'self' 'nonce-" + nonce + "' 'strict-dynamic';");

Cloudflare Workers:

const nonce = btoa(crypto.getRandomValues(new Uint8Array(16)).toString());
response.headers.set('Content-Security-Policy',
  `script-src 'self' 'nonce-${nonce}' 'strict-dynamic';`);

Step 2: Add the nonce to the Velaro script tag

<script nonce="YOUR_NONCE_HERE" src="https://cdn.velaro.com/embed/v2/loader.js"
  data-deployment-id="YOUR_DEPLOYMENT_ID">
</script>

The 'strict-dynamic' directive tells the browser to trust any scripts that this nonce-authorized script loads dynamically, so you only need the one nonce.

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';

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://cdn.velaro.com;
  style-src 'self' https://fonts.googleapis.com;
  font-src 'self' https://fonts.gstatic.com;
  connect-src 'self' https://api.velaro.com wss://api.velaro.com;
  img-src 'self' data: https://cdn.velaro.com;
  frame-src 'none';

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://cdn.velaro.com/embed/v2/loader.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://cdn.velaro.com or nonce to script-src |

| Widget loads but chat messages fail | Missing connect-src | Add https://api.velaro.com wss://api.velaro.com |

| Avatar or images broken | Missing img-src | Add https://cdn.velaro.com 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 |

You do NOT need 'unsafe-eval' or 'unsafe-inline' for Velaro.

Was this article helpful?