Brevo forms are one of those integrations that look simple until CSP blocks them in production.

You paste a signup form, refresh the page, and suddenly the console fills with violations for script-src, connect-src, or frame-src. If your policy is already fairly strict, Brevo will almost always need explicit allowlisting.

This guide is the practical version: what to add, what each directive is for, and a few copy-paste policies you can start from.

What Brevo forms usually need in CSP

Brevo form embeds commonly rely on:

  • JavaScript loaded from Brevo-controlled domains
  • XHR/fetch requests to Brevo APIs
  • inline styles or injected styles, depending on the embed type
  • form submission to Brevo endpoints
  • sometimes frames, depending on the integration

The exact hostnames can vary by embed method and account region, so you should always confirm in DevTools or violation reports. Still, there are a few patterns that come up often.

Typical directives involved:

  • script-src
  • connect-src
  • style-src
  • img-src
  • frame-src
  • form-action

If you already run a strict CSP with nonces and strict-dynamic, you need to be extra careful about how third-party scripts are introduced.

Start from your existing policy, not from scratch

If you already have a working CSP, extend it. Don’t throw it away just to make one form work.

For example, if your current header looks like this:

Content-Security-Policy: default-src 'self' https://www.googletagmanager.com https://*.cookiebot.com https://*.google-analytics.com; script-src 'self' 'nonce-YzMzYTgxYTUtYTMwOC00ZjE0LTkxODAtZjQyMjhlZWQxMDFm' 'strict-dynamic' https://www.googletagmanager.com https://*.cookiebot.com https://*.google-analytics.com; style-src 'self' 'unsafe-inline' https://www.googletagmanager.com https://*.cookiebot.com https://consent.cookiebot.com; img-src 'self' data: https:; font-src 'self'; connect-src 'self' https://api.headertest.com https://tallycdn.com https://or.headertest.com wss://or.headertest.com https://*.google-analytics.com https://*.googletagmanager.com https://*.cookiebot.com; frame-src 'self' https://consentcdn.cookiebot.com; frame-ancestors 'none'; base-uri 'self'; form-action 'self'; object-src 'none'

then the right move is to add only the Brevo sources you actually need.

Common Brevo source patterns

Brevo used to be known as Sendinblue, and both names can still show up in integrations, assets, or older code. In practice, you may need to allow one or more of these:

https://*.brevo.com
https://*.sendinblue.com
https://sibforms.com
https://*.sibforms.com

I’d treat these as candidates, not guaranteed requirements. Brevo has changed branding, but old infrastructure tends to stick around for a long time.

Minimal CSP additions for a Brevo form

If your page already has a CSP and you just need the extra Brevo allowances, this is the shape I’d start with:

Content-Security-Policy:
  default-src 'self';
  script-src 'self' https://sibforms.com https://*.sibforms.com https://*.brevo.com https://*.sendinblue.com;
  style-src 'self' 'unsafe-inline' https://sibforms.com https://*.brevo.com https://*.sendinblue.com;
  img-src 'self' data: https:;
  connect-src 'self' https://sibforms.com https://*.sibforms.com https://*.brevo.com https://*.sendinblue.com;
  frame-src 'self' https://sibforms.com https://*.sibforms.com;
  form-action 'self' https://sibforms.com https://*.sibforms.com https://*.brevo.com https://*.sendinblue.com;
  base-uri 'self';
  object-src 'none';

That’s intentionally broad enough to get most Brevo form embeds running, without falling back to https: everywhere.

Safer approach: merge Brevo into an existing strict policy

If you use nonces and strict-dynamic, keep them. Just extend the directives that actually need Brevo.

Here’s your sample policy updated with Brevo allowances:

Content-Security-Policy: default-src 'self' https://www.googletagmanager.com https://*.cookiebot.com https://*.google-analytics.com; script-src 'self' 'nonce-YzMzYTgxYTUtYTMwOC00ZjE0LTkxODAtZjQyMjhlZWQxMDFm' 'strict-dynamic' https://www.googletagmanager.com https://*.cookiebot.com https://*.google-analytics.com https://sibforms.com https://*.sibforms.com https://*.brevo.com https://*.sendinblue.com; style-src 'self' 'unsafe-inline' https://www.googletagmanager.com https://*.cookiebot.com https://consent.cookiebot.com https://sibforms.com https://*.brevo.com https://*.sendinblue.com; img-src 'self' data: https:; font-src 'self'; connect-src 'self' https://api.headertest.com https://tallycdn.com https://or.headertest.com wss://or.headertest.com https://*.google-analytics.com https://*.googletagmanager.com https://*.cookiebot.com https://sibforms.com https://*.sibforms.com https://*.brevo.com https://*.sendinblue.com; frame-src 'self' https://consentcdn.cookiebot.com https://sibforms.com https://*.sibforms.com; frame-ancestors 'none'; base-uri 'self'; form-action 'self' https://sibforms.com https://*.sibforms.com https://*.brevo.com https://*.sendinblue.com; object-src 'none'

A couple of notes:

  • I did not add Brevo hosts to default-src. I prefer explicit directives for third parties.
  • I left img-src 'self' data: https: alone because it already permits remote HTTPS images, which usually covers tracking pixels or remote assets.
  • style-src 'unsafe-inline' is already present in your policy. A lot of embed vendors still depend on that. I don’t love it, but I’m realistic about it.

Example: Nginx header

add_header Content-Security-Policy "default-src 'self'; script-src 'self' https://sibforms.com https://*.sibforms.com https://*.brevo.com https://*.sendinblue.com; style-src 'self' 'unsafe-inline' https://sibforms.com https://*.brevo.com https://*.sendinblue.com; img-src 'self' data: https:; connect-src 'self' https://sibforms.com https://*.sibforms.com https://*.brevo.com https://*.sendinblue.com; frame-src 'self' https://sibforms.com https://*.sibforms.com; form-action 'self' https://sibforms.com https://*.sibforms.com https://*.brevo.com https://*.sendinblue.com; base-uri 'self'; object-src 'none';" always;

Example: Apache

Header always set Content-Security-Policy "default-src 'self'; script-src 'self' https://sibforms.com https://*.sibforms.com https://*.brevo.com https://*.sendinblue.com; style-src 'self' 'unsafe-inline' https://sibforms.com https://*.brevo.com https://*.sendinblue.com; img-src 'self' data: https:; connect-src 'self' https://sibforms.com https://*.sibforms.com https://*.brevo.com https://*.sendinblue.com; frame-src 'self' https://sibforms.com https://*.sibforms.com; form-action 'self' https://sibforms.com https://*.sibforms.com https://*.brevo.com https://*.sendinblue.com; base-uri 'self'; object-src 'none';"

Example: Express with Helmet

import express from "express";
import helmet from "helmet";

const app = express();

app.use(
  helmet({
    contentSecurityPolicy: {
      directives: {
        defaultSrc: ["'self'"],
        scriptSrc: [
          "'self'",
          "https://sibforms.com",
          "https://*.sibforms.com",
          "https://*.brevo.com",
          "https://*.sendinblue.com"
        ],
        styleSrc: [
          "'self'",
          "'unsafe-inline'",
          "https://sibforms.com",
          "https://*.brevo.com",
          "https://*.sendinblue.com"
        ],
        imgSrc: ["'self'", "data:", "https:"],
        connectSrc: [
          "'self'",
          "https://sibforms.com",
          "https://*.sibforms.com",
          "https://*.brevo.com",
          "https://*.sendinblue.com"
        ],
        frameSrc: [
          "'self'",
          "https://sibforms.com",
          "https://*.sibforms.com"
        ],
        formAction: [
          "'self'",
          "https://sibforms.com",
          "https://*.sibforms.com",
          "https://*.brevo.com",
          "https://*.sendinblue.com"
        ],
        baseUri: ["'self'"],
        objectSrc: ["'none'"]
      }
    }
  })
);

Debugging by violation type

When Brevo breaks under CSP, the browser usually tells you exactly where.

script-src violations

You’ll see something like:

Refused to load the script 'https://...' because it violates the following Content Security Policy directive: "script-src ..."

Fix: add the exact Brevo host serving the script.

connect-src violations

Very common with form submission or validation requests.

Refused to connect to 'https://...' because it violates the following Content Security Policy directive: "connect-src ..."

Fix: add the request endpoint host to connect-src.

form-action violations

If the form submits directly to a Brevo endpoint, this one matters.

Refused to send form data to 'https://...' because it violates the following Content Security Policy directive: "form-action 'self'"

Fix: add the destination host to form-action.

frame-src violations

If the form or success flow uses an iframe:

Refused to frame 'https://...' because it violates the following Content Security Policy directive: "frame-src ..."

Fix: add the frame host to frame-src.

For production sites, I’d test Brevo with Content-Security-Policy-Report-Only first. That gives you real violations without breaking the form.

Example:

Content-Security-Policy-Report-Only: default-src 'self'; script-src 'self' https://sibforms.com https://*.sibforms.com https://*.brevo.com https://*.sendinblue.com; style-src 'self' 'unsafe-inline' https://sibforms.com https://*.brevo.com https://*.sendinblue.com; img-src 'self' data: https:; connect-src 'self' https://sibforms.com https://*.sibforms.com https://*.brevo.com https://*.sendinblue.com; frame-src 'self' https://sibforms.com https://*.sibforms.com; form-action 'self' https://sibforms.com https://*.sibforms.com https://*.brevo.com https://*.sendinblue.com; base-uri 'self'; object-src 'none';

Once the browser reports are clean, switch to enforcement.

My opinionated defaults

If I were adding Brevo to an otherwise locked-down site, I would:

  • keep default-src 'self'
  • explicitly allow Brevo only in the directives it needs
  • avoid broad https: in script-src and connect-src
  • keep object-src 'none'
  • keep base-uri 'self'
  • use form-action explicitly, because people forget it and then wonder why submission fails
  • test every embed in Report-Only first

That’s the difference between “CSP enabled” and “CSP actually maintained.”

Ready-to-use policy template

If you just want a clean starter policy for a page that hosts a Brevo form, use this:

Content-Security-Policy:
  default-src 'self';
  script-src 'self' https://sibforms.com https://*.sibforms.com https://*.brevo.com https://*.sendinblue.com;
  style-src 'self' 'unsafe-inline' https://sibforms.com https://*.brevo.com https://*.sendinblue.com;
  img-src 'self' data: https:;
  connect-src 'self' https://sibforms.com https://*.sibforms.com https://*.brevo.com https://*.sendinblue.com;
  frame-src 'self' https://sibforms.com https://*.sibforms.com;
  form-action 'self' https://sibforms.com https://*.sibforms.com https://*.brevo.com https://*.sendinblue.com;
  base-uri 'self';
  object-src 'none';

If you want more CSP starter patterns for common setups, csp-examples.com is handy. For Brevo-specific behavior, verify against the official Brevo documentation and the actual network requests your page makes.

The real trick with Brevo isn’t writing the first CSP. It’s resisting the lazy fix of script-src https: after the first violation. Don’t do that. Add the exact hosts, test, and keep the policy tight.