Adobe Typekit — now usually called Adobe Fonts — is one of those services that looks simple until CSP enters the room. You paste the embed code, the fonts load, everyone is happy. Then you lock down your site with Content Security Policy and suddenly your typography falls back to Arial.

I’ve had this break in production more than once. The annoying part is that Typekit needs more than one CSP directive, and the exact domains matter. If you only allow scripts, the stylesheet gets blocked. If you allow styles but forget fonts, the CSS loads and the fonts still fail. Classic.

Here’s how to set up CSP for Typekit properly, with examples you can actually use.

What Typekit needs in CSP

A standard Adobe Fonts embed usually looks like this:

<link rel="stylesheet" href="https://use.typekit.net/abcd123.css">

That CSS then pulls font files from Adobe-controlled domains, commonly:

  • https://use.typekit.net
  • https://p.typekit.net

Depending on your setup and Adobe’s delivery behavior, you may also see requests to:

  • https://use.typekit.com
  • https://fonts.adobe.com

For most real sites, the minimum working policy is:

  • style-src for the stylesheet from use.typekit.net
  • font-src for the actual font files from use.typekit.net and p.typekit.net

A good starting point:

Content-Security-Policy:
  default-src 'self';
  style-src 'self' https://use.typekit.net;
  font-src 'self' https://use.typekit.net https://p.typekit.net;
  object-src 'none';
  base-uri 'self';

That’s the short version. It works if your only Adobe Fonts integration is a stylesheet link.

The most common mistake

People often add Typekit to script-src. That usually does nothing useful.

If your site uses the stylesheet embed, the browser needs permission under style-src and font-src, not script-src.

Bad:

Content-Security-Policy:
  default-src 'self';
  script-src 'self' https://use.typekit.net;

Better:

Content-Security-Policy:
  default-src 'self';
  style-src 'self' https://use.typekit.net;
  font-src 'self' https://use.typekit.net https://p.typekit.net;

If you’re fuzzy on how directives inherit from default-src, csp-guide.com is a solid reference without too much fluff.

A stricter production policy

Most production sites need more than fonts, so here’s a more realistic baseline policy with Typekit added:

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

That keeps things tight while allowing Adobe Fonts to work.

If you use inline styles, be careful

Typekit itself usually doesn’t require 'unsafe-inline' in style-src when you’re using the CSS embed. That’s good news, because 'unsafe-inline' weakens CSP.

If your app already depends on inline styles from some framework or third-party widget, you might end up with this:

Content-Security-Policy:
  default-src 'self';
  style-src 'self' 'unsafe-inline' https://use.typekit.net;
  font-src 'self' https://use.typekit.net https://p.typekit.net;

That will work, but I avoid 'unsafe-inline' whenever I can. Don’t add it just because fonts are broken. Fonts usually break because font-src is missing.

Express.js example

Here’s a practical Express setup using helmet:

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

const app = express();

app.use(
  helmet({
    contentSecurityPolicy: {
      useDefaults: false,
      directives: {
        "default-src": ["'self'"],
        "script-src": ["'self'"],
        "style-src": ["'self'", "https://use.typekit.net"],
        "font-src": [
          "'self'",
          "https://use.typekit.net",
          "https://p.typekit.net"
        ],
        "img-src": ["'self'", "data:", "https:"],
        "connect-src": ["'self'"],
        "object-src": ["'none'"],
        "base-uri": ["'self'"],
        "form-action": ["'self'"],
        "frame-ancestors": ["'none'"]
      }
    }
  })
);

app.get("/", (req, res) => {
  res.send(`
    <!doctype html>
    <html>
      <head>
        <link rel="stylesheet" href="https://use.typekit.net/abcd123.css">
        <style>
          body { font-family: "proxima-nova", sans-serif; }
        </style>
      </head>
      <body>
        <h1>Typekit with CSP</h1>
      </body>
    </html>
  `);
});

app.listen(3000);

Two notes:

  1. The inline <style> block above would be blocked unless you allow 'unsafe-inline', use a hash, or move it to an external stylesheet.
  2. The Typekit stylesheet itself is external, so that part is fine with style-src https://use.typekit.net.

A cleaner version removes the inline style entirely:

<link rel="stylesheet" href="/app.css">
<link rel="stylesheet" href="https://use.typekit.net/abcd123.css">

Nginx example

If you’re setting headers at the edge, this is the shape you want:

add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' https://use.typekit.net; font-src 'self' https://use.typekit.net https://p.typekit.net; img-src 'self' data: https:; connect-src 'self'; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'none';" always;

That’s enough for most static sites using Adobe Fonts.

Report-Only first, then enforce

If you’re adding CSP to an existing site, don’t flip straight to blocking mode unless you enjoy emergency debugging.

Start with Content-Security-Policy-Report-Only:

Content-Security-Policy-Report-Only:
  default-src 'self';
  style-src 'self' https://use.typekit.net;
  font-src 'self' https://use.typekit.net https://p.typekit.net;
  report-to csp-endpoint;

Or the older report-uri form if you still need it:

Content-Security-Policy-Report-Only:
  default-src 'self';
  style-src 'self' https://use.typekit.net;
  font-src 'self' https://use.typekit.net https://p.typekit.net;
  report-uri https://example.com/csp-report;

That lets you see what would break before users see it.

How to verify your header

Don’t just eyeball the config and assume it’s correct. Check the actual response headers in the browser network panel or run the site through a header inspection tool. I like HeaderTest because it shows the real header as delivered, which is where a lot of “but it works locally” mistakes show up.

For reference, this is a real CSP header from headertest.com:

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

Notice the pattern: each resource type gets its own directive. That’s exactly why Typekit needs to be placed under the right ones instead of dumped into default-src and hoped for the best.

Debugging blocked Typekit requests

When Adobe Fonts fails under CSP, the browser console usually tells you exactly which directive blocked it.

A few examples:

Blocked stylesheet

You’ll see something like:

Refused to load the stylesheet 'https://use.typekit.net/abcd123.css'
because it violates the following Content Security Policy directive:
"style-src 'self'".

Fix:

style-src 'self' https://use.typekit.net;

Blocked font file

Typical error:

Refused to load the font 'https://p.typekit.net/p.css?...'
because it violates the following Content Security Policy directive:
"font-src 'self'".

Fix:

font-src 'self' https://use.typekit.net https://p.typekit.net;

Looks like a CORS issue but is really CSP

This one wastes time. Fonts sometimes look like a CORS problem in devtools, but the root cause is still font-src blocking the request. Check the CSP error first.

Should you allow wildcard Adobe domains?

Usually, no.

I’d rather allow the specific hosts I actually see in requests:

style-src 'self' https://use.typekit.net;
font-src 'self' https://use.typekit.net https://p.typekit.net;

You can loosen it:

style-src 'self' https://*.typekit.net;
font-src 'self' https://*.typekit.net;

But I wouldn’t start there unless Adobe changes delivery behavior and you need breathing room during rollout.

A complete example for a marketing site

Here’s a realistic policy for a site that uses self-hosted app code, Adobe Fonts, and standard HTTPS images:

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

And the matching HTML:

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>Adobe Fonts with CSP</title>
    <link rel="stylesheet" href="/assets/site.css">
    <link rel="stylesheet" href="https://use.typekit.net/abcd123.css">
  </head>
  <body>
    <main>
      <h1 class="headline">Secure fonts, no drama</h1>
      <p class="body">This page uses Adobe Fonts under a strict CSP.</p>
    </main>
  </body>
</html>

My default recommendation

If you’re adding CSP for Typekit today, I’d start with this and only widen it if the browser proves you need more:

Content-Security-Policy:
  default-src 'self';
  style-src 'self' https://use.typekit.net;
  font-src 'self' https://use.typekit.net https://p.typekit.net;
  object-src 'none';
  base-uri 'self';

That covers the common Adobe Fonts flow without opening up unrelated resource types.

If the site is bigger, split things properly across script-src, img-src, connect-src, and friends. Don’t use default-src as a dumping ground and don’t throw in 'unsafe-inline' because a font didn’t load. Nine times out of ten, the fix is just the right host in the right directive.