CSP and Trusted URLs
Salesforce sets a Content Security Policy header on Lightning pages. The framework
default is restrictive — script-src 'self', and font-src, img-src,
media-src, frame-src, style-src, and connect-src likewise scoped to the
org's own origin. Any external resource a component needs is blocked until an
administrator adds a Trusted URL (called a CSP Trusted Site in API 58.0 and
earlier) that grants that specific origin, that specific directive, in that
specific context.
Setup path, verbatim from Salesforce Help: From Setup, in the Quick Find box,
enter Trusted URLs, and then select Trusted URLs.
The metadata type is CspTrustedSite, stored in cspTrustedSites/ with the
.cspTrustedSite extension, available in API 39.0 and later.
Before Starting
Read the actual console message. It names the directive that blocked the
request, and that directive is the only checkbox you need. Configuring from a
guess is how allow-lists rot.
Establish which surfaces the component runs on. Lightning Experience,
Experience Builder site, and Visualforce are separate contexts, and a URL
trusted in one is not trusted in another.
Enumerate every origin the feature touches, not just the one in the first
error. A third-party SDK typically needs a loader host, an API host, a frame
host for challenge flows, an asset CDN, and a telemetry beacon.
Decide whether this is a browser problem at all. If the request originates
in Apex, you need a Remote Site Setting or a Named Credential, and no Trusted
URL will help.
Core Concepts
The six directives you can grant
CspTrustedSite exposes exactly six directive fields. There is no script-src
field — Lightning fixes script-src at 'self'.
| Field |
Grants |
Typical console phrase |
isApplicableToConnectSrc |
fetch, XMLHttpRequest, WebSockets |
Refused to connect to |
isApplicableToFrameSrc |
<iframe> sources |
Refused to frame |
isApplicableToImgSrc |
images |
Refused to load the image |
isApplicableToFontSrc |
fonts |
Refused to load the font |
isApplicableToStyleSrc |
stylesheets |
Refused to load the stylesheet |
isApplicableToMediaSrc |
audio and video resources |
Refused to load media |
Context scopes the whole record
context |
Applies to |
All |
All supported context types |
LEX |
Lightning Experience pages only |
Communities |
Experience Builder sites only |
VisualForce |
Custom Visualforce pages only, and only if the page's cspHeader attribute is true (API 55.0+) |
FieldServiceMobileExtension |
Field Service Mobile Extensions only (API 47.0+) |
LightningOut |
"Reserved for future use" (API 64.0+) |
The endpointUrl grammar
Must include a domain name; may include a port. Wildcards are supported
(*.example.com). Third-party APIs must begin with https://; WebSockets must
begin with wss://. The value is a literal — build any templated URL before you
write it into the metadata.
Malformed values (malformed^url.example.com, https://{subdomain}.example.com)
could be saved before February 2025 and are "excluded from generated CSP HTTP
headers" — an entry that looks Active and correct but has never been in the header.
The default for unset directives is version-dependent
| API version of the deployment |
If every isApplicable* is false |
| ≤ 49.0 |
All of them default to true — an open grant |
| 50.0–58.0 |
isApplicableToImgSrc is set to true |
| ≥ 59.0 |
At least one isApplicable* or canAccess* must be true, or the deploy is rejected |
Write every field explicitly, true and false alike.
Camera and microphone are Permissions-Policy, not CSP
canAccessCamera and canAccessMicrophone (API 59.0+) are inert unless the org has
enablePermissionsPolicy = true and grantCameraAccess / grantMicrophoneAccess
= TrustedUrls in SecuritySettings.
Trusted URL is not Remote Site Setting
|
Trusted URL |
Remote Site Setting / Named Credential |
| Enforced by |
The user's browser, via the CSP header |
The Salesforce application server |
| Governs |
LWC/Aura fetch, <img>, <iframe>, fonts, CSS, WebSockets |
Apex Http.send(), Visualforce |
| Error text |
Refused to ... violates the following Content Security Policy directive |
Unauthorized endpoint, please check Setup->Security->Remote site settings |
Common Patterns
Pattern A — one origin, one directive, one context
The default and the correct shape for most cases. Read the console error, create
one record with one directive ticked and every other field explicitly false, and
put the depending component's name in description. Example 1 in
references/examples.md.
Pattern B — vendor the library into a static resource
For third-party JavaScript, this is the answer rather than a Trusted URL, because
there is no script-src field to grant. Upload the library as a static resource
and load it with loadScript from lightning/platformResourceLoader; it is then
served from the org's own origin. This also pins the version.
Pattern C — one record per origin for a multi-host SDK
A payment or analytics SDK needs several entries — loader, API, challenge iframe,
asset CDN. Enumerate them from the vendor's own CSP documentation in one pass, then
test the failure and challenge paths, because a 3-D Secure iframe or an error
beacon fires only there.
Pattern D — Apex proxy instead of a browser call
When the call carries a credential, or the endpoint is internal and not
HTTPS-reachable from a browser, route it through Apex with a Named Credential. No
Trusted URL, no CORS, credentials never reach the browser.
Decision Guidance
| Situation |
Approach |
LWC needs to fetch an external JSON API |
Trusted URL, connect-src, correct context |
| Component needs a third-party JS library |
Static resource + loadScript — not a Trusted URL |
| Vendor mandates loading their script live |
Vendor conversation and a documented risk acceptance; there is no per-URL script-src grant |
| Third-party widget in an iframe |
Trusted URL, frame-src, plus connect-src for whatever the widget calls |
| Widget needs camera or microphone |
canAccess* on the URL and SecuritySettings set to TrustedUrls |
| Apex callout is blocked |
Remote Site Setting or Named Credential — not a Trusted URL |
| Internal service is HTTP-only |
Apex proxy; CSP requires HTTPS and there is no exemption |
| Vendor hostnames genuinely rotate |
Wildcard, with the reason recorded in description |
| Deadline pressure, widget still blocked |
Escalate the enumeration, never Relaxed CSP |
Recommended Workflow
- Reproduce and read the violation. Capture the full console message: it names
both the blocked URL and the directive. One message per blocked origin.
- Classify the caller. Browser-originated → Trusted URL. Apex-originated →
Remote Site Setting or Named Credential. A feature doing both needs both.
- Enumerate every origin the feature needs from the vendor's CSP
documentation, and note which directive each one requires. Do not configure
incrementally, one incident at a time.
- Decide the context from the surfaces the component runs on, and emit one
record per context rather than defaulting to
All.
- Author
CspTrustedSite metadata with every directive field written
explicitly, an exact host unless rotation genuinely requires a wildcard, and a
description naming the depending feature.
- Verify the whole flow, including the failure and challenge paths, and
confirm no residual console violations.
- Add the entry to the quarterly review list so it is removed when the feature
is retired — the allow-list must shrink as well as grow.
Review Checklist
Salesforce-Specific Gotchas
Full detail with quotes in references/gotchas.md.
- Context is not a label — a URL trusted in LEX is not trusted in Experience
Cloud, and the record still shows as Active.
- There is no
script-src checkbox. Use a static resource.
- Inline handlers are blocked and no configuration unblocks them.
- A malformed URL saves, shows Active, and is silently excluded from the CSP
header — the cause of "it's allow-listed and still blocked."
- The default for unset directives changed at API 50.0 and again at 59.0.
- Camera and microphone need an org-level switch too.
- A third-party script almost never talks to only its own origin.
- The generated CSP header has a practical size ceiling — keep it under 12 KB;
problems are reported approaching 16 KB.
- Relaxed CSP is site-wide and effectively permanent.
- HTTPS is mandatory, including for resources inside your own org.
- Trusted URLs and Remote Site Settings solve different problems.
Output Artifacts
| Artifact |
Description |
| Origin inventory |
Every host the feature touches, the directive each needs, and the vendor documentation it came from |
CspTrustedSite metadata |
One file per origin per context, every directive field explicit, description naming the dependent feature |
SecuritySettings change |
Only when camera or microphone access is required, deployed separately as an org-wide change |
| Verification note |
Which flows were exercised, including failure and challenge paths, and confirmation of a clean console |
| Review entry |
The record's name and owning feature added to the quarterly Trusted URL review |
Related Skills
lwc/static-resources-in-lwc — the loadScript / loadStyle path that replaces
a script-src grant, and how to package a vendored library
security/network-security-and-trusted-ips — the network-layer controls that
Trusted URLs are frequently confused with
integration/named-credentials-setup — the server-side alternative when the call
carries a credential or the endpoint cannot be browser-reachable
1---2name: csp-and-trusted-urls3description: Configure Content Security Policy via Trusted URLs and CSP Trusted Sites so Lightning, LWR, and LWC can call third-party scripts, APIs, and frame sources. NOT for clickjack configuration — use lwc/static-resources-in-lwc.4---56# CSP and Trusted URLs78Salesforce sets a Content Security Policy header on Lightning pages. The framework9default is restrictive — `script-src 'self'`, and `font-src`, `img-src`,10`media-src`, `frame-src`, `style-src`, and `connect-src` likewise scoped to the11org's own origin. Any external resource a component needs is blocked until an12administrator adds a **Trusted URL** (called a CSP Trusted Site in API 58.0 and13earlier) that grants that specific origin, that specific directive, in that14specific context.1516Setup path, verbatim from Salesforce Help: *From Setup, in the Quick Find box,17enter `Trusted URLs`, and then select **Trusted URLs**.*1819The metadata type is `CspTrustedSite`, stored in `cspTrustedSites/` with the20`.cspTrustedSite` extension, available in API 39.0 and later.2122---2324## Before Starting25261. **Read the actual console message.** It names the directive that blocked the27 request, and that directive is the only checkbox you need. Configuring from a28 guess is how allow-lists rot.29302. **Establish which surfaces the component runs on.** Lightning Experience,31 Experience Builder site, and Visualforce are separate contexts, and a URL32 trusted in one is not trusted in another.33343. **Enumerate every origin the feature touches**, not just the one in the first35 error. A third-party SDK typically needs a loader host, an API host, a frame36 host for challenge flows, an asset CDN, and a telemetry beacon.37384. **Decide whether this is a browser problem at all.** If the request originates39 in Apex, you need a Remote Site Setting or a Named Credential, and no Trusted40 URL will help.4142---4344## Core Concepts4546### The six directives you can grant4748`CspTrustedSite` exposes exactly six directive fields. There is **no `script-src`49field** — Lightning fixes `script-src` at `'self'`.5051| Field | Grants | Typical console phrase |52|---|---|---|53| `isApplicableToConnectSrc` | `fetch`, `XMLHttpRequest`, WebSockets | `Refused to connect to` |54| `isApplicableToFrameSrc` | `<iframe>` sources | `Refused to frame` |55| `isApplicableToImgSrc` | images | `Refused to load the image` |56| `isApplicableToFontSrc` | fonts | `Refused to load the font` |57| `isApplicableToStyleSrc` | stylesheets | `Refused to load the stylesheet` |58| `isApplicableToMediaSrc` | audio and video resources | `Refused to load media` |5960### Context scopes the whole record6162| `context` | Applies to |63|---|---|64| `All` | All supported context types |65| `LEX` | Lightning Experience pages only |66| `Communities` | Experience Builder sites only |67| `VisualForce` | Custom Visualforce pages only, **and only if the page's `cspHeader` attribute is `true`** (API 55.0+) |68| `FieldServiceMobileExtension` | Field Service Mobile Extensions only (API 47.0+) |69| `LightningOut` | "Reserved for future use" (API 64.0+) |7071### The `endpointUrl` grammar7273Must include a domain name; may include a port. Wildcards are supported74(`*.example.com`). Third-party APIs must begin with `https://`; WebSockets must75begin with `wss://`. The value is a literal — build any templated URL before you76write it into the metadata.7778Malformed values (`malformed^url.example.com`, `https://{subdomain}.example.com`)79could be saved before February 2025 and are "excluded from generated CSP HTTP80headers" — an entry that looks Active and correct but has never been in the header.8182### The default for unset directives is version-dependent8384| API version of the deployment | If every `isApplicable*` is `false` |85|---|---|86| ≤ 49.0 | All of them default to `true` — an **open** grant |87| 50.0–58.0 | `isApplicableToImgSrc` is set to `true` |88| ≥ 59.0 | At least one `isApplicable*` or `canAccess*` must be `true`, or the deploy is rejected |8990Write every field explicitly, `true` and `false` alike.9192### Camera and microphone are `Permissions-Policy`, not CSP9394`canAccessCamera` and `canAccessMicrophone` (API 59.0+) are inert unless the org has95`enablePermissionsPolicy = true` and `grantCameraAccess` / `grantMicrophoneAccess`96= `TrustedUrls` in `SecuritySettings`.9798### Trusted URL is not Remote Site Setting99100| | Trusted URL | Remote Site Setting / Named Credential |101|---|---|---|102| Enforced by | The user's **browser**, via the CSP header | The Salesforce **application server** |103| Governs | LWC/Aura `fetch`, `<img>`, `<iframe>`, fonts, CSS, WebSockets | Apex `Http.send()`, Visualforce |104| Error text | `Refused to ... violates the following Content Security Policy directive` | `Unauthorized endpoint, please check Setup->Security->Remote site settings` |105106---107108## Common Patterns109110### Pattern A — one origin, one directive, one context111112The default and the correct shape for most cases. Read the console error, create113one record with one directive ticked and every other field explicitly `false`, and114put the depending component's name in `description`. Example 1 in115[`references/examples.md`](references/examples.md).116117### Pattern B — vendor the library into a static resource118119For third-party JavaScript, this is the answer rather than a Trusted URL, because120there is no `script-src` field to grant. Upload the library as a static resource121and load it with `loadScript` from `lightning/platformResourceLoader`; it is then122served from the org's own origin. This also pins the version.123124### Pattern C — one record per origin for a multi-host SDK125126A payment or analytics SDK needs several entries — loader, API, challenge iframe,127asset CDN. Enumerate them from the vendor's own CSP documentation in one pass, then128test the *failure and challenge* paths, because a 3-D Secure iframe or an error129beacon fires only there.130131### Pattern D — Apex proxy instead of a browser call132133When the call carries a credential, or the endpoint is internal and not134HTTPS-reachable from a browser, route it through Apex with a Named Credential. No135Trusted URL, no CORS, credentials never reach the browser.136137---138139## Decision Guidance140141| Situation | Approach |142|---|---|143| LWC needs to `fetch` an external JSON API | Trusted URL, `connect-src`, correct context |144| Component needs a third-party JS library | Static resource + `loadScript` — not a Trusted URL |145| Vendor mandates loading their script live | Vendor conversation and a documented risk acceptance; there is no per-URL `script-src` grant |146| Third-party widget in an iframe | Trusted URL, `frame-src`, plus `connect-src` for whatever the widget calls |147| Widget needs camera or microphone | `canAccess*` on the URL **and** `SecuritySettings` set to `TrustedUrls` |148| Apex callout is blocked | Remote Site Setting or Named Credential — not a Trusted URL |149| Internal service is HTTP-only | Apex proxy; CSP requires HTTPS and there is no exemption |150| Vendor hostnames genuinely rotate | Wildcard, with the reason recorded in `description` |151| Deadline pressure, widget still blocked | Escalate the enumeration, never Relaxed CSP |152153---154155## Recommended Workflow1561571. **Reproduce and read the violation.** Capture the full console message: it names158 both the blocked URL and the directive. One message per blocked origin.1592. **Classify the caller.** Browser-originated → Trusted URL. Apex-originated →160 Remote Site Setting or Named Credential. A feature doing both needs both.1613. **Enumerate every origin the feature needs** from the vendor's CSP162 documentation, and note which directive each one requires. Do not configure163 incrementally, one incident at a time.1644. **Decide the context** from the surfaces the component runs on, and emit one165 record per context rather than defaulting to `All`.1665. **Author `CspTrustedSite` metadata** with every directive field written167 explicitly, an exact host unless rotation genuinely requires a wildcard, and a168 `description` naming the depending feature.1696. **Verify the whole flow**, including the failure and challenge paths, and170 confirm no residual console violations.1717. **Add the entry to the quarterly review list** so it is removed when the feature172 is retired — the allow-list must shrink as well as grow.173174---175176## Review Checklist177178- [ ] The directive granted matches the directive named in the console error179- [ ] Every `isApplicableTo*` field is written explicitly, `true` or `false`180- [ ] `context` is set deliberately, not defaulted181- [ ] `endpointUrl` is an exact host unless a wildcard is justified in `description`182- [ ] `endpointUrl` has no template placeholders and no malformed characters183- [ ] `description` names the component or feature that depends on this entry184- [ ] Every origin the feature touches has an entry — loader, API, frame, CDN, beacon185- [ ] Third-party JavaScript is a static resource, not a live CDN load186- [ ] Camera/microphone grants are paired with the `SecuritySettings` change187- [ ] No Relaxed CSP anywhere in the solution188- [ ] Failure and challenge paths exercised, not just the happy path189- [ ] Entry recorded for quarterly review190191---192193## Salesforce-Specific Gotchas194195Full detail with quotes in [`references/gotchas.md`](references/gotchas.md).1961971. **Context is not a label** — a URL trusted in LEX is not trusted in Experience198 Cloud, and the record still shows as Active.1992. **There is no `script-src` checkbox.** Use a static resource.2003. **Inline handlers are blocked** and no configuration unblocks them.2014. **A malformed URL saves, shows Active, and is silently excluded** from the CSP202 header — the cause of "it's allow-listed and still blocked."2035. **The default for unset directives changed at API 50.0 and again at 59.0.**2046. **Camera and microphone need an org-level switch too.**2057. **A third-party script almost never talks to only its own origin.**2068. **The generated CSP header has a practical size ceiling** — keep it under 12 KB;207 problems are reported approaching 16 KB.2089. **Relaxed CSP is site-wide and effectively permanent.**20910. **HTTPS is mandatory**, including for resources inside your own org.21011. **Trusted URLs and Remote Site Settings solve different problems.**211212---213214## Output Artifacts215216| Artifact | Description |217|---|---|218| Origin inventory | Every host the feature touches, the directive each needs, and the vendor documentation it came from |219| `CspTrustedSite` metadata | One file per origin per context, every directive field explicit, `description` naming the dependent feature |220| `SecuritySettings` change | Only when camera or microphone access is required, deployed separately as an org-wide change |221| Verification note | Which flows were exercised, including failure and challenge paths, and confirmation of a clean console |222| Review entry | The record's name and owning feature added to the quarterly Trusted URL review |223224---225226## Related Skills227228- `lwc/static-resources-in-lwc` — the `loadScript` / `loadStyle` path that replaces229 a `script-src` grant, and how to package a vendored library230- `security/network-security-and-trusted-ips` — the network-layer controls that231 Trusted URLs are frequently confused with232- `integration/named-credentials-setup` — the server-side alternative when the call233 carries a credential or the endpoint cannot be browser-reachable