Figma embeds are one of those things that look trivial until your CSP blocks them and you end up staring at a blank iframe.

If you’re embedding a Figma file, prototype, or board on your site, the CSP piece is usually small but annoyingly specific. The main directive you need is frame-src. Sometimes you also need to think about child-src, frame-ancestors, and the fact that Figma itself loads its own resources inside the iframe, not in your page context.

Here’s the practical rule:

  • Your page needs permission to load a Figma iframe
  • Figma’s internal scripts, styles, and network calls are governed by Figma’s CSP, not yours
  • Your CSP only controls whether the browser is allowed to embed https://www.figma.com/... at all

The minimum CSP for a Figma embed

If your page already has a CSP and you want to allow Figma embeds, this is the smallest useful addition:

Content-Security-Policy: default-src 'self'; frame-src https://www.figma.com;

That allows iframes from Figma and nothing else beyond your own origin by default.

A matching HTML embed might look like this:

<iframe
  style="border: 1px solid rgba(0, 0, 0, 0.1);"
  width="800"
  height="450"
  src="https://www.figma.com/embed?embed_host=share&url=https://www.figma.com/file/FILE_ID/Example"
  allowfullscreen>
</iframe>

If frame-src does not include https://www.figma.com, the browser will block it.

The directive that matters: frame-src

For Figma embeds, frame-src is the directive you almost always need.

Content-Security-Policy: frame-src https://www.figma.com;

If your policy uses default-src and does not specify frame-src, then frames inherit from default-src. That means this policy will block Figma:

Content-Security-Policy: default-src 'self';

Because https://www.figma.com is not 'self'.

If you want a simple working baseline:

Content-Security-Policy:
  default-src 'self';
  frame-src 'self' https://www.figma.com;
  object-src 'none';
  base-uri 'self';

I like this pattern because it stays tight while still being obvious to the next developer.

child-src vs frame-src

Some older CSP setups still use child-src. Modern browsers use frame-src for frames, but if you support legacy behavior or inherited policies from older apps, you may want both.

Content-Security-Policy:
  default-src 'self';
  child-src https://www.figma.com;
  frame-src https://www.figma.com;

If you’re cleaning up an old policy, I’d keep frame-src as the source of truth and only include child-src if you know you need backward compatibility.

For deeper directive behavior, the CSP reference at csp-guide.com is useful.

Copy-paste policies

1. Minimal Figma embed policy

Use this if the page is mostly static and only needs the iframe.

Content-Security-Policy:
  default-src 'self';
  frame-src https://www.figma.com;
  object-src 'none';
  base-uri 'self';

2. Typical app page with scripts, styles, and Figma

Use this if your app already has local JS and CSS.

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

3. Based on a real production-style header

You gave a real header from headertest.com. If you want to adapt that kind of policy to support Figma, the change is small: add Figma to frame-src.

Original style:

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

Updated for Figma embeds:

Content-Security-Policy:
  default-src 'self' https://www.googletagmanager.com https://*.cookiebot.com https://*.google-analytics.com;
  script-src 'self' 'nonce-OGJjYWVkNmUtMjUzMC00M2Y0LWJiN2EtZGJjNTM5Yzg2MDAx' '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 https://www.figma.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 Figma?

Usually, no.

This trips people up a lot. The Figma app running inside the iframe makes its own requests under Figma’s origin and CSP. Your page does not need connect-src https://www.figma.com just to display the embed.

You only need to expand connect-src if your own page JavaScript talks to Figma APIs directly.

Example:

fetch("https://api.figma.com/v1/files/FILE_ID", {
  headers: {
    "X-Figma-Token": "YOUR_TOKEN"
  }
});

That would require something like:

Content-Security-Policy: connect-src 'self' https://api.figma.com;

But that is unrelated to the iframe embed itself.

frame-ancestors does not allow Figma embeds

frame-ancestors is for controlling who can embed your page.

I still see developers try this:

Content-Security-Policy: frame-ancestors https://www.figma.com;

That does not permit loading a Figma iframe on your page.

  • frame-src = what your page may embed
  • frame-ancestors = who may embed your page

If your site should never be embedded by anyone, keep this:

Content-Security-Policy: frame-ancestors 'none';

That is perfectly compatible with embedding Figma on your own page.

Common mistakes

Mistake 1: Only updating default-src

Some teams assume this is enough:

Content-Security-Policy: default-src 'self' https://www.figma.com;

It can work if frame-src is absent, but I don’t like relying on fallback behavior when the intent is specifically about frames. Be explicit:

Content-Security-Policy:
  default-src 'self';
  frame-src 'self' https://www.figma.com;

Mistake 2: Using the wrong Figma origin

Use:

frame-src https://www.figma.com;

Not random subdomains unless you have a specific reason. Keep it narrow.

Mistake 3: Trying to fix the embed with script-src

Your page does not execute Figma’s iframe scripts in your origin, so this does nothing for the embedding problem:

script-src 'self' https://www.figma.com;

If the iframe is blocked, the fix is almost always frame-src.

Mistake 4: Forgetting report-only rollout

If you’re changing CSP on a production app, test with report-only first.

Content-Security-Policy-Report-Only:
  default-src 'self';
  frame-src 'self' https://www.figma.com;
  report-to csp-endpoint;

That lets you see violations before enforcing them. The reporting setup depends on your stack, but the rollout strategy is worth it.

Official CSP docs are here: MDN Content-Security-Policy

Nginx example

add_header Content-Security-Policy "default-src 'self'; frame-src 'self' https://www.figma.com; object-src 'none'; base-uri 'self';" always;

Apache example

Header always set Content-Security-Policy "default-src 'self'; frame-src 'self' https://www.figma.com; object-src 'none'; base-uri 'self';"

Express / Node example

app.use((req, res, next) => {
  res.setHeader(
    "Content-Security-Policy",
    "default-src 'self'; frame-src 'self' https://www.figma.com; object-src 'none'; base-uri 'self';"
  );
  next();
});

Helmet example

import helmet from "helmet";

app.use(
  helmet.contentSecurityPolicy({
    directives: {
      defaultSrc: ["'self'"],
      frameSrc: ["'self'", "https://www.figma.com"],
      objectSrc: ["'none'"],
      baseUri: ["'self'"]
    }
  })
);

Fast troubleshooting checklist

If the Figma embed is blank or blocked, I check these first:

  1. Browser console says CSP blocked a frame
  2. frame-src is missing https://www.figma.com
  3. A stricter meta CSP is overriding the header
  4. The embed URL is malformed
  5. The Figma resource itself does not allow the kind of sharing you expect

A good final baseline for most sites is this:

Content-Security-Policy:
  default-src 'self';
  script-src 'self';
  style-src 'self' 'unsafe-inline';
  img-src 'self' data: https:;
  font-src 'self';
  connect-src 'self';
  frame-src 'self' https://www.figma.com;
  object-src 'none';
  base-uri 'self';
  form-action 'self';
  frame-ancestors 'none';

If all you need is Figma embeds, don’t overcomplicate it. Add https://www.figma.com to frame-src, keep the rest of the policy tight, and move on.