Bunny Fonts is one of the easier font providers to support in Content Security Policy, mostly because the setup is simple and the domains are predictable.

If you just want the working policy, here it is:

Content-Security-Policy: default-src 'self'; style-src 'self' https://fonts.bunny.net; font-src 'self' https://fonts.bunny.net; img-src 'self' data:; script-src 'self'; connect-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'none';

That covers the common case where your page loads CSS from Bunny Fonts and then loads font files from the same host.

The two Bunny Fonts CSP directives that matter

For Bunny Fonts, you usually need:

  • style-src for the stylesheet URL
  • font-src for the actual font files

Typical Bunny Fonts embed code looks like this:

<link rel="preconnect" href="https://fonts.bunny.net">
<link href="https://fonts.bunny.net/css?family=inter:400,500,700" rel="stylesheet" />

That means your CSP needs to allow https://fonts.bunny.net in style-src.

Then the CSS returned by Bunny Fonts references font files, also from Bunny Fonts, so font-src needs the same host.

Minimal CSP for Bunny Fonts

Use this when your site is otherwise pretty locked down:

Content-Security-Policy: default-src 'self'; style-src 'self' https://fonts.bunny.net; font-src 'self' https://fonts.bunny.net; object-src 'none'; base-uri 'self'; frame-ancestors 'none';

That’s the smallest sane starting point.

If you already have a CSP, add Bunny Fonts to it

Most teams are not starting from scratch. They already have a CSP header and just need to add Bunny Fonts without breaking everything else.

Given a real-world policy like this:

content-security-policy: default-src 'self' https://www.googletagmanager.com https://*.cookiebot.com https://*.google-analytics.com; script-src 'self' 'nonce-MmU2NDBmOGEtN2YwMy00Y2RlLWExNjEtNmYzYzUwOTE0OGE4' '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'

You’d update only the directives Bunny Fonts needs:

Content-Security-Policy: default-src 'self' https://www.googletagmanager.com https://*.cookiebot.com https://*.google-analytics.com; script-src 'self' 'nonce-MmU2NDBmOGEtN2YwMy00Y2RlLWExNjEtNmYzYzUwOTE0OGE4' '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 https://fonts.bunny.net; img-src 'self' data: https:; font-src 'self' https://fonts.bunny.net; 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'

That’s usually all you need.

Do you need connect-src for Bunny Fonts?

Usually, no.

Bunny Fonts are typically loaded through:

  • a stylesheet request
  • one or more font file requests

Those are covered by style-src and font-src, not connect-src.

I see people cargo-cult font domains into connect-src all the time. Don’t. Keep the policy tight.

Do you need preconnect allowances in CSP?

No separate CSP directive is needed for this HTML:

<link rel="preconnect" href="https://fonts.bunny.net">

CSP doesn’t have a preconnect-src directive. The actual resource fetches still need to match style-src and font-src.

Copy-paste examples

Plain HTML site

Content-Security-Policy: default-src 'self'; style-src 'self' https://fonts.bunny.net; font-src 'self' https://fonts.bunny.net; img-src 'self' data:; script-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'none';
<link rel="preconnect" href="https://fonts.bunny.net">
<link href="https://fonts.bunny.net/css?family=inter:400,500,700" rel="stylesheet">

Nginx

add_header Content-Security-Policy "default-src 'self'; style-src 'self' https://fonts.bunny.net; font-src 'self' https://fonts.bunny.net; img-src 'self' data:; script-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'none';" always;

Apache

Header always set Content-Security-Policy "default-src 'self'; style-src 'self' https://fonts.bunny.net; font-src 'self' https://fonts.bunny.net; img-src 'self' data:; script-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'none';"

Express.js with Helmet

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

const app = express();

app.use(
  helmet({
    contentSecurityPolicy: {
      directives: {
        defaultSrc: ["'self'"],
        scriptSrc: ["'self'"],
        styleSrc: ["'self'", "https://fonts.bunny.net"],
        fontSrc: ["'self'", "https://fonts.bunny.net"],
        imgSrc: ["'self'", "data:"],
        objectSrc: ["'none'"],
        baseUri: ["'self'"],
        frameAncestors: ["'none'"],
      },
    },
  })
);

Next.js custom headers

// next.config.js
const csp = [
  "default-src 'self'",
  "script-src 'self'",
  "style-src 'self' https://fonts.bunny.net",
  "font-src 'self' https://fonts.bunny.net",
  "img-src 'self' data:",
  "object-src 'none'",
  "base-uri 'self'",
  "frame-ancestors 'none'",
].join("; ");

module.exports = {
  async headers() {
    return [
      {
        source: "/(.*)",
        headers: [
          {
            key: "Content-Security-Policy",
            value: csp,
          },
        ],
      },
    ];
  },
};

Common breakages and fixes

Error: refused to load stylesheet from fonts.bunny.net

You forgot style-src.

Broken policy:

Content-Security-Policy: default-src 'self'; font-src 'self' https://fonts.bunny.net;

Fixed policy:

Content-Security-Policy: default-src 'self'; style-src 'self' https://fonts.bunny.net; font-src 'self' https://fonts.bunny.net;

Error: refused to load font from fonts.bunny.net

You allowed the CSS but not the font files.

Broken policy:

Content-Security-Policy: default-src 'self'; style-src 'self' https://fonts.bunny.net; font-src 'self';

Fixed policy:

Content-Security-Policy: default-src 'self'; style-src 'self' https://fonts.bunny.net; font-src 'self' https://fonts.bunny.net;

Error even though default-src 'self' is set

default-src is just a fallback. Once you define style-src or font-src, those specific directives take over.

This catches people all the time. If style-src exists, the browser uses that for stylesheets. It does not merge with default-src.

If you want a deeper directive-by-directive breakdown, the docs at csp-guide.com are useful.

Should you use unsafe-inline for Bunny Fonts?

No.

Bunny Fonts itself does not require unsafe-inline. If your current CSP includes it, that’s probably for some other part of your app, not fonts.

This is a good split to keep in mind:

  • external Bunny Fonts stylesheet: style-src https://fonts.bunny.net
  • inline <style> blocks or style="" attributes: may require nonce, hash, or unsafe-inline

Don’t blame the font provider for your inline CSS debt.

Self-hosting Bunny Fonts files

If you download and serve the fonts yourself, CSP gets simpler.

Example:

Content-Security-Policy: default-src 'self'; style-src 'self'; font-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'none';

And your HTML becomes:

<link rel="stylesheet" href="/assets/fonts/inter.css">

I generally like self-hosting when I want fewer third-party dependencies and fewer CSP exceptions. The tradeoff is you now own updates, caching, and asset packaging.

Report-Only first if you’re changing production CSP

If you’re adding Bunny Fonts to an existing app with analytics, tag managers, consent tools, and legacy frontend code, don’t flip the enforcing header blindly.

Start with report-only:

Content-Security-Policy-Report-Only: default-src 'self'; style-src 'self' https://fonts.bunny.net; font-src 'self' https://fonts.bunny.net; object-src 'none'; base-uri 'self'; frame-ancestors 'none';

Then watch what the browser complains about before enforcing it.

If the site is fairly standard and doesn’t need weird exceptions, I’d ship this:

Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self' https://fonts.bunny.net; font-src 'self' https://fonts.bunny.net; img-src 'self' data: https:; connect-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'none'; form-action 'self';

Why this one?

  • strict enough to be useful
  • explicit about fonts
  • doesn’t add random allowances you don’t need
  • easy to extend when marketing asks for three trackers and a chat widget next week

That’s really the whole Bunny Fonts CSP story: allow the stylesheet host in style-src, allow the font host in font-src, and don’t loosen anything else unless you actually have to.