Refused to connect to <url> because it violates the following Content Security Policy directive
The Salesforce Lightning runtime blocks browser-side fetches to URLs not explicitly allowed in the org's Content Security Policy (CSP). Add the target URL as a CSP Trusted Site (or use a Named Credential and route via Apex) so the browser is allowed to call it.
Also seen asContent Security Policy·Refused to connect·violates the following Content Security Policy·CSP violation lightning
You shipped a Lightning Web Component that embeds a third-party charting library hitting an external API. The component works fine in your scratch org. The first user opens it in production and the browser console fills with Refused to connect to 'https://api.charting-vendor.com' because it violates the following Content Security Policy directive. The chart never renders. The user files a ticket.
What the platform is actually enforcing
Salesforce wraps every Lightning page in a strict Content Security Policy. The browser receives a Content-Security-Policy header on every page load that lists which external origins JavaScript on the page is allowed to call. Any callout to an origin not on the allowed list is blocked at the browser level before the network request leaves the user's machine.
The policy covers several directives. connect-src controls XHR and fetch calls. img-src controls image sources. frame-src controls iframe sources. script-src controls where JavaScript can be loaded from. style-src controls stylesheet origins. font-src controls web fonts. Each directive has its own list of allowed origins.
The default list is restrictive on purpose. Salesforce wants to prevent malicious code (whether intentional or accidental) from exfiltrating data to arbitrary external endpoints. The policy is one of the most important defenses against cross-site script attacks on the Lightning platform.
The broken example
A Lightning Web Component that fetches a stock chart:
// stockChart.js
import { LightningElement, api } from 'lwc';
export default class StockChart extends LightningElement {
@api symbol;
data;
async connectedCallback() {
const response = await fetch(
`https://api.charting-vendor.com/quotes?symbol=${this.symbol}`
);
this.data = await response.json();
}
}
<!-- stockChart.html -->
<template>
<template if:true={data}>
<p>{data.price}</p>
</template>
</template>
In a scratch org with permissive CSP defaults, this might work for an admin running a dev session. In production, the browser blocks the fetch call with the CSP message and the component shows blank.
A second shape: an attempt to load an external script via loadScript:
import { loadScript } from 'lightning/platformResourceLoader';
import chartjs from 'https://cdn.jsdelivr.net/npm/chart.js';
export default class StockChart extends LightningElement {
async connectedCallback() {
await loadScript(this, chartjs);
}
}
loadScript doesn't accept URLs from arbitrary CDNs. The script source must be a static resource hosted in your org. Lightning Web Security blocks anything else with a CSP message even before the network call.
A third shape, an inline iframe:
<template>
<iframe src="https://app.charting-vendor.com/embed"></iframe>
</template>
Iframes are subject to frame-src. By default, no external origins are allowed for iframes embedded in Lightning pages. The iframe shows as a blank box.
Three paths to a fix
Add the origin to CSP Trusted Sites. Setup, then CSP Trusted Sites. Click New, give the entry a name (Charting Vendor), put the full origin in Trusted Site URL (https://api.charting-vendor.com), choose which contexts the site is trusted in (Lightning Experience, Lightning Communities, etc.), and pick which CSP directives to allow (connect-src, img-src, frame-src, etc.). Save. The browser receives the updated policy on the next page load and the fetch succeeds.
Use a Named Credential for the callout, not a direct fetch. For Apex-side callouts to external APIs, a Named Credential bundles the endpoint, authentication, and CSP considerations into one configuration. The browser never directly hits the external API; Apex does, server-to-server, which sidesteps CSP entirely. Suitable for APIs where you don't need the browser to call them directly.
Host the external script as a static resource. Download the third-party library, upload it as a Static Resource in Setup, and load it with loadScript(this, MY_STATIC_RESOURCE). The script is now served from Salesforce's domain and CSP doesn't apply. Versioning is your responsibility.
The fixed example
The same chart component, working under CSP:
// stockChart.js
import { LightningElement, api } from 'lwc';
import getQuote from '@salesforce/apex/StockController.getQuote';
export default class StockChart extends LightningElement {
@api symbol;
data;
error;
async connectedCallback() {
try {
this.data = await getQuote({ symbol: this.symbol });
} catch (e) {
this.error = e.body?.message ?? e.message;
}
}
}
// StockController.cls
public with sharing class StockController {
@AuraEnabled(cacheable=true)
public static Quote getQuote(String symbol) {
Http http = new Http();
HttpRequest req = new HttpRequest();
req.setEndpoint('callout:Charting_Vendor/quotes?symbol=' + EncodingUtil.urlEncode(symbol, 'UTF-8'));
req.setMethod('GET');
HttpResponse res = http.send(req);
if (res.getStatusCode() != 200) {
throw new AuraHandledException('Quote fetch failed: ' + res.getStatusCode());
}
return (Quote) JSON.deserialize(res.getBody(), Quote.class);
}
public class Quote {
@AuraEnabled public String symbol;
@AuraEnabled public Decimal price;
@AuraEnabled public Datetime asOf;
}
}
Charting_Vendor is a Named Credential configured in Setup with the endpoint base URL and authentication. The browser never sees the external origin. CSP doesn't fire.
When the use case genuinely requires the browser to hit an external URL (a third-party widget, an embedded analytics dashboard), add the origin to CSP Trusted Sites:
Name: Charting Vendor API
URL: https://api.charting-vendor.com
Active: true
Context: Lightning Experience
CSP Directives:
connect-src: yes
img-src: no
frame-src: no
Reload the page and the fetch goes through.
Lightning Web Security vs Lightning Locker
Salesforce ships two security architectures for Lightning Web Components. Lightning Locker (the legacy) and Lightning Web Security (the modern replacement, default for new orgs since Summer '22). They differ in how strictly they enforce CSP.
Lightning Web Security delegates to the browser's native CSP enforcement, which is stricter and more standards-compliant. Lightning Locker had its own internal sandbox that sometimes allowed things native CSP would block.
If your component works in a Locker-mode org and fails in an LWS-mode org, the LWS-mode enforcement is correct. Fix the CSP violation rather than trying to revert.
Check which mode your org is in: Setup, Session Settings, look for "Use Lightning Web Security for Lightning Web Components and Aura". If it's checked, you're on LWS.
CSP and the cookie-domain story
Salesforce serves Lightning Experience from your My Domain origin (acme.lightning.force.com) and serves component bundles from a separate origin (acme.lightning.force.com or lightning.force.com depending on configuration). The CSP applied to one origin doesn't automatically apply to the other. If you're embedding a Lightning component into a Visualforce page or vice versa, the CSP applied is whichever page is the outermost frame.
This matters for legacy components that lived under Visualforce. The Visualforce CSP is configurable separately (Setup, Session Settings, "Content Security Policy") and the Lightning CSP is governed by CSP Trusted Sites.
When unsafe-inline is the culprit
Some violations don't mention an external origin. The browser reports Refused to execute inline script because it violates CSP directive: script-src ... 'unsafe-inline'. This means your component has inline JavaScript (a <script> tag with code inside, or an onclick attribute with code) that the CSP doesn't allow.
Lightning Web Components don't normally have inline scripts. If you see this, you've probably tried to dangerously inject HTML via innerHTML or used a wired template that the platform rewrote. Refactor to use the LWC programming model (event listeners declared in JavaScript, not HTML; templates that don't contain executable code).
Diagnosing in production
When a user reports a missing chart or a broken integration:
- Have the user open the browser dev tools (Cmd+Option+I on Mac, F12 on Windows) and reproduce the issue.
- Look at the Console tab for a red message starting with
Refused to. - The message names the directive being violated and the origin being blocked.
- Cross-reference against CSP Trusted Sites in Setup.
If the origin isn't in CSP Trusted Sites, add it. If it is in CSP Trusted Sites but a specific directive is missing, edit and check the missing directive.
What CSP doesn't protect
CSP protects against unauthorized client-side network calls. It does not protect against:
- Server-side callouts from Apex (those have their own Remote Site Settings, separate)
- Direct database access by code in your org (governed by sharing and FLS)
- User-initiated navigation to external URLs (anchors are not blocked)
- Data exfiltration through forms with action attributes (the FORM submission is not blocked the same way)
CSP is one layer. Sharing, FLS, profile permissions, IP-based login restrictions, MFA, and Remote Site Settings are others.
Remote Site Settings for Apex callouts
Apex code making HTTP callouts to external URLs needs the origin listed in Remote Site Settings (Setup, then Remote Site Settings). This is a separate list from CSP Trusted Sites.
If a Named Credential is used, the origin is implicitly trusted via the credential and you don't need a separate Remote Site Setting. If you're calling Http.send with a hardcoded URL, you need the Remote Site Setting.
Don't conflate the two. CSP Trusted Sites controls what the browser can hit. Remote Site Settings controls what Apex can hit. A component that uses both directions (browser to one origin, Apex to another) needs both configured.
Test patterns for CSP-aware components
Two tests catch CSP regressions before production:
A component-render test that runs in a CSP-strict environment (a Connected App configured with LWS, or a scratch org with the LWS flag set). Tests that pass in this environment will also pass in production.
A CSP-violation log scrape that runs against a recent UAT environment after deploy. The Salesforce Event Monitoring CSP Violation event captures every browser CSP violation. Querying this in a post-deploy smoke test reveals new violations introduced by the deploy.
List<CSPEventLogFile__c> recent = [
SELECT Id, BlockedURI__c, Directive__c, Source__c
FROM CSPEventLogFile__c
WHERE EventDate >= :Datetime.now().addHours(-1)
LIMIT 100
];
If new violations show up in the hour after deploy, investigate.
A note on iframes and same-origin
Iframes loading content from your org (<iframe src="/apex/MyPage">) are subject to frame-ancestors and similar policies, not just frame-src. Visualforce pages need their own allow list of which origins can embed them; this is configured per-page in the page's metadata via clickjacking protection settings.
When embedding a Visualforce page inside a Lightning Web Component, both the LWC's CSP (controlling what the LWC can do) and the Visualforce page's clickjacking protection (controlling what can embed Visualforce) must allow the combination.
Habits that prevent CSP pain
Inventory every external origin your org's components call before deploy. Maintain a list in your team wiki, and treat additions to that list as a security review item rather than a routine config change. A new external origin in CSP Trusted Sites is a new place from which data can leave your org, so the decision to add it deserves a moment of thought.
For every new component that touches an external service, ask three questions: Does this need to be browser-side or can it be Apex-side? If browser-side, what origin does it hit? Have I added that origin to CSP Trusted Sites? Asking these at code-review time catches most issues before they reach production. A reviewer who has answered these questions three or four times starts asking them automatically.
Prefer Apex callouts with Named Credentials over direct browser fetch when the data flow allows. CSP-free, central auth, easy to audit. The Named Credential carries the endpoint and authentication in one configuration item; the Apex code reads it transparently and the change-control story is cleaner than scattered fetch calls across components.
When pulling in third-party JS libraries, ship them as Static Resources rather than relying on a CDN. The build pipeline can pin a specific version, and CSP doesn't enter the picture. The trade-off is that you take on responsibility for keeping the static resource up to date; the benefit is a deterministic deploy artifact that doesn't depend on a third-party CDN being healthy at runtime.
Further reading from Salesforce
- Lightning Web Components Developer Guide: Content Security Policy
- Salesforce Help: CSP Trusted Sites
- Lightning Components Developer Guide: Lightning Web Security
- Apex Developer Guide: Named Credentials
- Trailhead: Lightning Web Components Security
Related dictionary terms
Share this fix
Related Lightning · LWC errors
@wire(getRecord, ...) requires the fields property
Lightning · LWCYou used `getRecord` from `lightning/uiRecordApi` without specifying which fields to fetch. LDS doesn't auto-fetch all fields like an old-sc…
Action failed: c:myComponent$controller$myAction [<JS error message>]
Lightning · LWCAn Aura component's client-side JavaScript controller threw an exception. The error message is wrapped in `Action failed: c:component$contro…
Cannot find component: c:myComponent
Lightning · LWCThe Lightning runtime tried to instantiate a component by name and the platform doesn't have it — the bundle didn't deploy, the namespace pr…
Cannot read properties of undefined (reading 'X') — in LWC component lifecycle
Lightning · LWCPlain JavaScript TypeError, but in LWC it almost always means a `@wire` result was read in `connectedCallback` or `renderedCallback` before …
Lightning component cannot be created: c:myComponent
Lightning · LWCThe framework couldn't instantiate a Lightning component at runtime — usually a missing dependency, a permission gap, or a static-resource l…