ApexCharts is one of those libraries that looks harmless until you turn on a strict Content Security Policy and parts of your charts suddenly stop rendering.
I’ve hit this a few times in dashboards where everything worked fine locally, then failed in staging because CSP blocked inline styles or scripts. ApexCharts itself is pretty friendly compared to some older charting libraries, but you still need to account for how it injects styles and how your app loads it.
This guide is the practical version: what to allow, what to avoid, and copy-paste CSP examples you can actually ship.
The short version
If you use ApexCharts in a normal app, these are the CSP directives that usually matter:
script-srcfor the ApexCharts JavaScript bundlestyle-srcbecause chart libraries often apply inline stylesimg-srcif you use data URIs or export featuresconnect-srcif you fetch chart data from an APIfont-srcif your app loads custom fonts used in chart labels
A minimal CSP for a self-hosted ApexCharts setup often looks like this:
Content-Security-Policy:
default-src 'self';
script-src 'self';
style-src 'self' 'unsafe-inline';
img-src 'self' data:;
font-src 'self';
connect-src 'self';
object-src 'none';
base-uri 'self';
frame-ancestors 'none';
That style-src 'unsafe-inline' is the part most security teams dislike. Fair enough. But in practice, it’s often the first thing you need to make chart rendering behave.
Why ApexCharts can trigger CSP issues
ApexCharts renders charts with SVG and DOM manipulation. The trouble usually comes from one of these:
-
Inline styles Charts often set styles directly on elements.
-
Inline initialization code If you initialize charts with a raw
<script>block in your HTML, CSP will block it unless you use a nonce or hash. -
Remote data If the chart fetches data from an API,
connect-srcneeds to allow that endpoint. -
Exports and embedded images If your chart uses data URLs for generated images,
img-src data:may be required.
Safe baseline: self-hosted ApexCharts
If you bundle ApexCharts with your app or serve it from your own origin, start here:
Content-Security-Policy:
default-src 'self';
script-src 'self';
style-src 'self' 'unsafe-inline';
img-src 'self' data:;
connect-src 'self';
font-src 'self';
object-src 'none';
base-uri 'self';
frame-ancestors 'none';
form-action 'self';
This assumes:
apexcharts.jsis loaded from your own domain- chart data comes from your own backend
- you don’t rely on third-party analytics or tag managers
Example: basic HTML page with ApexCharts
Here’s a simple page that will work with the policy above.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>ApexCharts CSP Demo</title>
<script src="/js/apexcharts.min.js" defer></script>
</head>
<body>
<div id="chart"></div>
<script nonce="{{ .CSPNonce }}">
document.addEventListener('DOMContentLoaded', function () {
const options = {
chart: {
type: 'bar',
height: 350
},
series: [{
name: 'Sales',
data: [30, 40, 35, 50, 49, 60]
}],
xaxis: {
categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun']
}
};
const chart = new ApexCharts(document.querySelector("#chart"), options);
chart.render();
});
</script>
</body>
</html>
And the matching CSP header:
Content-Security-Policy:
default-src 'self';
script-src 'self' 'nonce-{{RANDOM_NONCE}}';
style-src 'self' 'unsafe-inline';
img-src 'self' data:;
connect-src 'self';
font-src 'self';
object-src 'none';
base-uri 'self';
frame-ancestors 'none';
If you’re using a nonce, every inline script that should run needs the matching nonce value.
If you load ApexCharts from a CDN
I generally prefer self-hosting for CSP and reliability, but if you use a CDN, explicitly allow it in script-src.
Content-Security-Policy:
default-src 'self';
script-src 'self' https://cdn.jsdelivr.net 'nonce-{{RANDOM_NONCE}}';
style-src 'self' 'unsafe-inline';
img-src 'self' data:;
connect-src 'self';
font-src 'self';
object-src 'none';
base-uri 'self';
frame-ancestors 'none';
Example HTML:
<script src="https://cdn.jsdelivr.net/npm/apexcharts" defer></script>
<script nonce="{{ .CSPNonce }}">
document.addEventListener('DOMContentLoaded', function () {
new ApexCharts(document.querySelector("#chart"), {
chart: { type: 'line' },
series: [{ name: 'Visitors', data: [12, 18, 14, 22, 29] }],
xaxis: { categories: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri'] }
}).render();
});
</script>
The biggest gotcha: style-src
This is where most ApexCharts CSP breakage shows up.
A lot of frontend teams try this:
style-src 'self';
Then the chart renders half-broken, labels look wrong, or animations fail.
The pragmatic fix is:
style-src 'self' 'unsafe-inline';
That’s not ideal, but it’s common for UI libraries that manipulate styles in the DOM.
If your org has a hard rule against 'unsafe-inline', test ApexCharts very aggressively before committing to it. Depending on your exact integration and version, you may need architectural changes, not just policy tweaks.
For a deeper breakdown of style-src, the official CSP docs are useful, and https://csp-guide.com has a solid explanation of common tradeoffs.
Fetching chart data from an API
Most real charts are not hardcoded. They fetch JSON from an API, which means connect-src matters.
Frontend code:
async function renderChart() {
const response = await fetch('https://api.example.com/metrics/sales');
const data = await response.json();
const chart = new ApexCharts(document.querySelector('#chart'), {
chart: { type: 'area' },
series: [{
name: 'Revenue',
data: data.values
}],
xaxis: {
categories: data.labels
}
});
chart.render();
}
CSP:
Content-Security-Policy:
default-src 'self';
script-src 'self' 'nonce-{{RANDOM_NONCE}}';
style-src 'self' 'unsafe-inline';
img-src 'self' data:;
connect-src 'self' https://api.example.com;
font-src 'self';
object-src 'none';
base-uri 'self';
frame-ancestors 'none';
If you use WebSockets for live chart updates:
connect-src 'self' https://api.example.com wss://ws.example.com;
A stricter production template
If you want something closer to a production-ready policy, this is a decent starting point:
Content-Security-Policy:
default-src 'self';
script-src 'self' 'nonce-{{RANDOM_NONCE}}';
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;
font-src 'self';
connect-src 'self' https://api.example.com;
object-src 'none';
base-uri 'self';
frame-ancestors 'none';
form-action 'self';
report-to default-endpoint;
report-uri /csp-report;
The important bit is that this stays focused. Don’t throw random hosts into default-src and hope that covers everything. Be explicit.
Adapting a real-world CSP header
Here’s the real CSP header you supplied from headertest.com:
content-security-policy:
default-src 'self' https://www.googletagmanager.com https://*.cookiebot.com https://*.google-analytics.com;
script-src 'self' 'nonce-OWRiYzdmOWYtMzMwNS00MDE4LTg0ZjEtNDU2ODU5ZWEyZGM3' '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'
To add ApexCharts to a setup like that, if it’s self-hosted, you may not need to change much at all. You already have:
script-src 'self'style-src 'self' 'unsafe-inline'img-src 'self' data: https:connect-srcsupport for APIs
That means ApexCharts will usually fit into this policy without drama.
If you fetch chart data from https://charts.headertest.com, then add it to connect-src:
connect-src 'self' https://api.headertest.com https://charts.headertest.com https://tallycdn.com https://or.headertest.com wss://or.headertest.com https://*.google-analytics.com https://*.googletagmanager.com https://*.cookiebot.com;
If you load ApexCharts from a CDN, add that host to script-src:
script-src 'self' 'nonce-OWRiYzdmOWYtMzMwNS00MDE4LTg0ZjEtNDU2ODU5ZWEyZGM3' 'strict-dynamic' https://cdn.jsdelivr.net https://www.googletagmanager.com https://*.cookiebot.com https://*.google-analytics.com;
I would still self-host it if possible. Fewer moving parts, fewer CSP exceptions.
Framework examples
React with bundled ApexCharts
If you bundle ApexCharts through your build tool, CSP is simpler because the library ships as part of your app bundle.
Content-Security-Policy:
default-src 'self';
script-src 'self';
style-src 'self' 'unsafe-inline';
img-src 'self' data:;
connect-src 'self' https://api.example.com;
object-src 'none';
base-uri 'self';
frame-ancestors 'none';
Next.js or server-rendered apps with inline bootstrap code
Use a nonce for any inline script:
<script nonce={nonce}
dangerouslySetInnerHTML={{
__html: `
window.__CHART_DATA__ = ${JSON.stringify(chartData)};
`
}}
/>
Header:
Content-Security-Policy:
default-src 'self';
script-src 'self' 'nonce-{{RANDOM_NONCE}}';
style-src 'self' 'unsafe-inline';
img-src 'self' data:;
connect-src 'self' https://api.example.com;
object-src 'none';
base-uri 'self';
frame-ancestors 'none';
Common CSP errors with ApexCharts
Refused to execute inline script
Your inline chart initialization is blocked.
Fix it by:
- moving code into an external JS file allowed by
script-src, or - adding a nonce
Example:
script-src 'self' 'nonce-{{RANDOM_NONCE}}';
Refused to apply inline style
Your style-src is too strict.
Usually this fixes it:
style-src 'self' 'unsafe-inline';
Refused to connect to API endpoint
Your chart data source is not in connect-src.
Example:
connect-src 'self' https://api.example.com;
Chart export or embedded image blocked
Add data URIs to img-src:
img-src 'self' data:;
My recommended policy for most ApexCharts apps
If you want the practical default, use this and tighten only after testing:
Content-Security-Policy:
default-src 'self';
script-src 'self' 'nonce-{{RANDOM_NONCE}}';
style-src 'self' 'unsafe-inline';
img-src 'self' data:;
font-src 'self';
connect-src 'self' https://api.example.com;
object-src 'none';
base-uri 'self';
frame-ancestors 'none';
form-action 'self';
That covers the usual ApexCharts setup without pretending every app can avoid inline styles.
If you’re debugging a broken chart under CSP, check these in order:
script-srcstyle-srcconnect-srcimg-src
That sequence catches most failures fast.
For directive-by-directive details, the official CSP documentation is still the source of truth, and https://csp-guide.com is useful when you want cleaner explanations than the spec gives you.