CORS
CORS (Cross-Origin Resource Sharing) in Salesforce is a Setup feature where an admin allowlists external web origins that are permitted to call supported Salesforce APIs from JavaScript running in a browser.
Definition
CORS (Cross-Origin Resource Sharing) in Salesforce is a Setup feature where an admin allowlists external web origins that are permitted to call supported Salesforce APIs from JavaScript running in a browser. To reach Salesforce APIs, Apex REST resources, and Lightning Out from JavaScript on another domain, you add that origin to the org's CORS allowlist. If the origin is not in the allowlist, Salesforce returns HTTP status code 403 and the browser blocks the response.
CORS exists because browsers enforce the same-origin policy to stop a malicious site from reading data from another domain using a logged-in user's session. Salesforce honors this. The allowlist tells Salesforce to answer a browser's preflight check with headers that let the real request proceed. CORS does not replace authentication. A call from an allowlisted origin still needs a valid session ID or OAuth access token. CORS only tells the browser that this Salesforce org accepts cross-origin requests from this specific origin.
How the CORS allowlist works in Salesforce
The Setup page and the origin URL pattern
You manage the allowlist from Setup. Enter CORS in the Quick Find box, select CORS, then select New and type an origin URL pattern. Each entry is a full origin, meaning the scheme plus the domain and an optional port, not a path. The pattern must include the HTTPS protocol unless you are pointing at localhost during development. A valid entry looks like https://app.example.com or https://app.example.com:8443. Behind the scenes each row is a CorsWhitelistEntry record, and the same list is editable through the Metadata API as CorsWhitelistOrigin, so you can source-control it and deploy it between orgs. That matters because the allowlist is org configuration, not code, and it is easy to forget when you promote an integration from a sandbox to production. The change takes effect on the next API call, so there is no deploy step or cache to clear. Keep the entries tight. An origin you no longer use should be removed, because every live entry is a domain you have told the browser is allowed to talk to your org on a user's behalf.
The preflight request and the response headers
Most Salesforce API calls are not simple requests, because they carry an Authorization header. For those, the browser first sends an OPTIONS preflight to ask whether the real call is allowed. Salesforce checks the request's Origin header against the allowlist. If the origin matches, Salesforce returns Access-Control-Allow-Origin set to that origin, along with the other CORS headers that name the methods and request headers the browser may use. The browser then sends the actual GET or POST. If the origin is not in the allowlist, Salesforce responds with HTTP 403 and the browser refuses to hand the response to your script. A useful mental model is that two separate checks run. The browser asks Salesforce whether the origin is trusted, and that is CORS. Salesforce separately asks whether the caller is authenticated, and that is OAuth or the session ID. Both must pass. A common point of confusion is seeing a 403 or a blocked message and assuming the token is wrong, when the real problem is just a missing allowlist entry.
What CORS does not do
CORS controls the browser, not server-side authentication, and that distinction saves a lot of debugging time. A request from an allowlisted origin still needs a valid session ID or OAuth access token to authenticate and to be authorized for the data it touches. Adding an origin does not grant any access on its own. In the other direction, a request from an origin that is not allowlisted is blocked by the browser before your code can read the result, even if the token is perfectly valid. The most important consequence is that server-to-server calls ignore CORS completely. Code running in Node, Python, Java, or any backend is not a browser, so the same-origin policy never applies. If you see a backend hostname or a server IP address sitting in the CORS allowlist, someone added it by mistake. It has no effect on that traffic and only adds noise to a security-sensitive list. CORS is also unrelated to Remote Site Settings, which govern outbound Apex callouts from Salesforce to an external endpoint. The two are often confused because both deal with cross-domain traffic.
Which Salesforce APIs honor the allowlist
The allowlist applies when JavaScript in a browser calls a Salesforce endpoint from another origin. That covers the REST API, the User Interface API, Apex REST resources you expose with the RestResource annotation, Bulk API 2.0, and the Lightning Out hosted services that let you drop a Lightning component into an external page. The Connect REST API follows the same rule for browser callers. Pages and components that run inside Salesforce do not need an entry, because they are same-origin with the org. A Visualforce page, an Aura component, or a Lightning Web Component hosted in your Lightning Experience org is served from your My Domain and talks to the same domain, so the browser never treats it as cross-origin. Experience Cloud sites have their own CORS handling tied to the site's domain, so a custom front end calling into a community is configured there rather than only in the org's main CORS list. When you are unsure whether an entry is needed, ask one question. Is the JavaScript loaded from a different origin than the Salesforce endpoint it calls? If yes, you need an allowlist entry.
Wildcard subdomains for multi-environment apps
Salesforce supports a single wildcard in the origin pattern, and the wildcard must sit in front of a second-level domain. The entry https://*.example.com matches https://app.example.com, https://staging.example.com, and any other direct subdomain of example.com. This is handy for a product that runs the same code across several environments, since one entry can cover dev, staging, and production rather than three separate rows. The wildcard is restricted to that leftmost position. A mid-string pattern like https://app.*.example.com is not valid, and you cannot wildcard the second-level domain itself. Use the wildcard with care, because it is broader than a single host. Any subdomain that resolves under example.com, including one created later, will be trusted to call your org from a browser. If your environments live under a domain you do not fully control, prefer explicit per-host entries so a new subdomain cannot quietly inherit access. For a domain you own end to end, the wildcard is usually the cleaner and more maintainable choice, and it removes the risk of forgetting to add a new environment's exact host.
Reading common CORS errors
Browser CORS messages are blunt, so learning to read them shortens the fix. The classic one is blocked by CORS policy, with a note that no Access-Control-Allow-Origin header is present on the requested resource. That almost always means the calling origin is missing from the allowlist, or it was entered with the wrong scheme or port. Add the exact origin the browser reports and try again. A second message, that the preflight request does not pass the access control check, often points at a header the actual request will send that the server did not approve in its Allow-Headers response. Standard headers are handled automatically, but an unusual custom header can trip this. A third trap is mixing up CORS with authentication. A 401 means the token or session is the problem and CORS is fine, while a 403 from the API for a cross-origin call usually means the origin itself was rejected. Check the failing request in the browser network tab, confirm the Origin header value, and match it character for character against the allowlist entry, including https and any port.
CORS with headless LWC and external front ends
The allowlist matters most in modern architectures where a front end lives outside Salesforce and calls back in. A Lightning Web Component built with LWC Open Source, or a React or plain JavaScript app hosted on your own domain, runs in the browser and is cross-origin to your My Domain endpoint. Without an allowlist entry, every API call fails at the browser even though OAuth would have succeeded, and the first error you see is the opaque CORS block rather than a helpful authentication message. The practical fix is to configure CORS before you write the integration code, so a runtime failure can only be about the token, not the origin. Pair this with a Connected App that defines the OAuth client your front end uses, and decide how the access token is obtained and stored. For a public single-page app that means a flow suited to browsers, since there is no safe place to keep a client secret. Set the allowlist, the Connected App, and the token flow together. When all three line up, a headless Salesforce front end behaves predictably, and CORS stops being the thing that blocks your first request.
How to add an origin to the CORS allowlist
Add an external origin to the org's CORS allowlist so JavaScript on that domain can call supported Salesforce APIs from a browser. You configure this in Setup; it takes effect on the next API call.
- Open the CORS Setup page
From Setup, type CORS in the Quick Find box and select CORS. The page lists existing allowed origins and lets you add new ones.
- Create a new origin entry
Select New, then enter the origin URL pattern. Include the HTTPS scheme and the domain, for example https://app.example.com, with an optional port. Use https://*.example.com to cover all direct subdomains.
- Save and verify from the browser
Save the entry. Trigger an API call from your external page and confirm the request succeeds and that Salesforce returns the Access-Control-Allow-Origin header for your origin in the network tab.
- Pair it with authentication
The allowlist only satisfies the browser. Set up a Connected App and an OAuth flow so the call also carries a valid access token, otherwise the request is allowed cross-origin but still fails on auth.
The full origin: scheme plus domain and optional port, no path. Must use HTTPS unless it targets localhost. Match it exactly to what the browser sends.
A single * in front of a second-level domain, such as https://*.example.com, covers all direct subdomains. Mid-string wildcards are not allowed.
Each entry maps to CorsWhitelistEntry and deploys as CorsWhitelistOrigin, so you can source-control the list and promote it between orgs.
- A missing entry surfaces as a browser CORS block or HTTP 403, which is easy to misread as an authentication failure.
- Server-to-server callers (Node, Python, any backend) ignore CORS entirely. Do not add backend hostnames or IPs to the allowlist.
- The origin must match exactly, including https and any port. A scheme or port mismatch silently fails the check.
- A subdomain wildcard trusts every current and future subdomain of that domain, so use it only on a domain you fully control.
Prefer this walkthrough as its own page? How to CORS in Salesforce, step by step
Trust & references
Cross-checked against the following references.
Straight from the source - Salesforce's reference material on CORS.
Hands-on resources to go deeper on CORS.
About the Author
Dipojjal Chakrabarti is a B2C Solution Architect with 29 Salesforce certifications and over 13 years in the Salesforce ecosystem. He runs salesforcedictionary.com to help admins, developers, architects, and cert/interview candidates sharpen their fundamentals. More about Dipojjal.
Test your knowledge
Q1. What does the CORS Setup page in Salesforce control?
Q2. What happens without a CORS allowlist entry for an external web origin?
Q3. Where do administrators add origin URLs to the Salesforce CORS allowlist?
Discussion
Loading discussion…