Heroicons are the easy case for CSP, which is nice for once.
If you use Heroicons the normal way — React components, Vue components, Blade components, or copied inline SVG markup — Heroicons do not require their own domain in your Content Security Policy. There’s no Heroicons CDN you need to allow. No remote script. No remote font. Usually no remote image either.
That means your CSP for Heroicons is mostly about how you render the SVGs.
The short answer
Most Heroicons setups work with a CSP like this:
Content-Security-Policy:
default-src 'self';
script-src 'self';
style-src 'self';
img-src 'self' data:;
object-src 'none';
base-uri 'self';
frame-ancestors 'none';
If your icons are rendered as inline <svg>, this usually works fine.
Do Heroicons need CSP allowlisting?
Usually, no.
Heroicons are commonly used in a few ways:
-
Imported as framework components
- React
- Vue
-
Copied as inline SVG
- pasted directly into HTML templates
-
Wrapped in your own UI component library
- still inline SVG under the hood
-
Saved as local SVG files
- served from your own origin
Every one of those is compatible with a strict CSP because the browser is not fetching code or assets from Heroicons infrastructure.
What CSP directives matter for Heroicons?
These are the directives I actually care about for icon rendering:
img-srcstyle-srcscript-srcdefault-src
And sometimes:
object-srcbase-uri
If you want the CSP docs for directive behavior, the official reference is here:
For a cleaner CSP-focused breakdown, https://csp-guide.com is also useful.
Case 1: Heroicons as inline SVG
This is the most common setup.
Example:
<button class="icon-button" aria-label="Search">
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
aria-hidden="true"
class="size-6"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="m21 21-4.35-4.35m0 0A7.5 7.5 0 1 0 5.5 5.5a7.5 7.5 0 0 0 11.15 11.15Z"
/>
</svg>
</button>
CSP:
Content-Security-Policy:
default-src 'self';
script-src 'self';
style-src 'self';
img-src 'self' data:;
object-src 'none';
base-uri 'self';
frame-ancestors 'none';
Why this works:
- Inline SVG in the HTML document is not blocked like inline JavaScript.
- The SVG markup is part of the DOM.
- Heroicons do not require
script-srcexceptions. - Heroicons do not require
font-srcexceptions.
Case 2: Heroicons in React
Typical usage:
import { MagnifyingGlassIcon } from '@heroicons/react/24/outline'
export function SearchButton() {
return (
<button type="button" aria-label="Search">
<MagnifyingGlassIcon className="h-6 w-6" />
</button>
)
}
CSP:
Content-Security-Policy:
default-src 'self';
script-src 'self';
style-src 'self';
img-src 'self' data:;
object-src 'none';
base-uri 'self';
frame-ancestors 'none';
Same story: the icon becomes inline SVG in the rendered DOM.
If your React app injects styles at runtime, that’s a framework/tooling CSP issue, not a Heroicons issue. I see people blame the icon package when the real problem is CSS-in-JS needing a nonce or hash.
Case 3: Heroicons in Vue
<script setup>
import { HomeIcon } from '@heroicons/vue/24/solid'
</script>
<template>
<nav>
<HomeIcon class="h-5 w-5" aria-hidden="true" />
</nav>
</template>
CSP:
Content-Security-Policy:
default-src 'self';
script-src 'self';
style-src 'self';
img-src 'self' data:;
object-src 'none';
base-uri 'self';
frame-ancestors 'none';
Again, no Heroicons-specific sources needed.
Case 4: Heroicons saved as local SVG files
If you export or store SVG files in your own app and reference them like this:
<img src="/icons/arrow-right.svg" alt="" />
Then your CSP needs to allow your own origin in img-src:
Content-Security-Policy:
default-src 'self';
script-src 'self';
style-src 'self';
img-src 'self' data:;
object-src 'none';
base-uri 'self';
frame-ancestors 'none';
That’s already covered by 'self'.
If you host icons on a separate asset domain, add it:
Content-Security-Policy:
default-src 'self';
script-src 'self';
style-src 'self';
img-src 'self' data: https://assets.example.com;
object-src 'none';
base-uri 'self';
frame-ancestors 'none';
What usually breaks is not Heroicons
I’ve debugged a bunch of “CSP broke our icons” reports. Most turned out to be one of these:
1. CSS classes not applying
The SVG renders, but it has no size, wrong color, or weird alignment.
That’s usually style-src, your CSS pipeline, or missing stylesheet loading.
Bad assumption:
style-src 'none';
More realistic:
style-src 'self';
If you rely on inline styles, that’s where the CSP fight starts. Heroicons themselves don’t need inline styles.
2. Data URL icons are blocked
Some apps convert SVGs to data: URLs and use them in <img> or CSS backgrounds.
Example:
<img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0i..." alt="" />
Then you need:
img-src 'self' data:;
Without data:, those icons fail.
3. SVG loaded through CSS background images
Example:
.icon-search {
background-image: url('/icons/search.svg');
}
That still falls under asset loading, usually img-src in modern CSP behavior for images. Keep it simple:
img-src 'self' data:;
style-src 'self';
4. You embedded SVG with <object> or <embed>
Don’t do this for icons.
<object data="/icons/search.svg" type="image/svg+xml"></object>
A sane CSP often includes:
object-src 'none';
That will block it, and honestly that’s good. Use inline SVG or <img>.
Copy-paste policies
Minimal CSP for Heroicons
Use this if your app is simple and Heroicons are inline or component-based:
Content-Security-Policy:
default-src 'self';
script-src 'self';
style-src 'self';
img-src 'self' data:;
object-src 'none';
base-uri 'self';
frame-ancestors 'none';
Strict CSP for Heroicons with nonce-based scripts
If your app already uses nonces:
Content-Security-Policy:
default-src 'self';
script-src 'self' 'nonce-{RANDOM_NONCE}' 'strict-dynamic';
style-src 'self';
img-src 'self' data:;
object-src 'none';
base-uri 'self';
form-action 'self';
frame-ancestors 'none';
Heroicons don’t care about the nonce. Your JavaScript does.
CSP for Heroicons plus analytics and consent tooling
Using the real-world header pattern you provided as a base, here’s a cleaned-up version where Heroicons need nothing extra:
Content-Security-Policy:
default-src 'self' https://www.googletagmanager.com https://*.cookiebot.com https://*.google-analytics.com;
script-src 'self' 'nonce-{RANDOM_NONCE}' '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.example.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';
Heroicons still don’t need:
https://heroicons.com- a CDN source
font-srcexceptions- script exceptions
Express example
app.use((req, res, next) => {
res.setHeader(
'Content-Security-Policy',
[
"default-src 'self'",
"script-src 'self'",
"style-src 'self'",
"img-src 'self' data:",
"object-src 'none'",
"base-uri 'self'",
"frame-ancestors 'none'",
].join('; ')
)
next()
})
Nginx example
add_header Content-Security-Policy "
default-src 'self';
script-src 'self';
style-src 'self';
img-src 'self' data:;
object-src 'none';
base-uri 'self';
frame-ancestors 'none';
" always;
Apache example
Header always set Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:; object-src 'none'; base-uri 'self'; frame-ancestors 'none';"
Debugging checklist
If Heroicons appear broken under CSP, I check these in order:
- Are the icons inline SVG or external files?
- Is the browser console showing a real CSP violation?
- Is the failure actually CSS-related rather than SVG-related?
- Are you using
data:URLs for icons? - Did someone use
<object>for SVG? - Did your framework inject inline styles or scripts that need separate CSP handling?
That last one gets people all the time.
Common bad fixes
Adding random external domains
I see this a lot:
img-src 'self' https://heroicons.com;
script-src 'self' https://heroicons.com;
That’s cargo-cult CSP. Heroicons don’t need it.
Allowing 'unsafe-inline' for scripts
Also common:
script-src 'self' 'unsafe-inline';
If your only goal is “make icons work,” this is the wrong fix. Heroicons are not blocked because of inline script rules.
Overbroad default-src
default-src * data: blob:;
Sure, the icons will probably work. So will a bunch of things you didn’t mean to allow.
Practical recommendation
If you use Heroicons as React/Vue components or pasted SVG markup, start here:
Content-Security-Policy:
default-src 'self';
script-src 'self';
style-src 'self';
img-src 'self' data:;
object-src 'none';
base-uri 'self';
frame-ancestors 'none';
Then expand the policy only for the other stuff in your stack: analytics, consent managers, APIs, CDNs, live reload, whatever.
That’s the whole trick with Heroicons and CSP: keep them local, render them as SVG, and don’t overcomplicate the policy.