> ## Content Index
> Fetch the complete content index at: https://notes.hiddenbay.tech/llms.txt
> Use this file to discover other available public pages before exploring further.

# Understanding How Experience Cloud Sites Access Data
- URL: https://notes.hiddenbay.tech/experience-cloud-endpoints/
- Published: 2026-09-28T04:00:36.000Z
- Updated: 2026-09-28T04:00:35.000Z
- Author: Daniel Harfleet
- Tags: Experience Cloud Security

A Salesforce Experience Cloud site doesn't send data-populated pages to the browser. It sends a shell. The browser then asks for the data separately, over APIs that are still there when the browser isn't.

You don't need to be in a browser to call those APIs and if the features or data they expose are available to guest users, they will also be available when you call the API. This means that putting components onto a page is not the on-off switch of that data being available.

Advice lands differently depending on whether you understand why you're being asked to follow it. Knowing the mechanics of how things fit together is what makes official guidance make sense, not just something to comply with. Official guidance followed without that understanding gets skipped the first time it's inconvenient.

So this article spends more time than most on mechanism, so you can understand the system context, the attack surfaces available and then why the documented advise is the best way to secure it.

---

## The page and the data arrive separately

Most of us built our mental model of web applications on the classic request-response pattern. You ask for a page, the server queries the data, merges it into a page template, and sends you finished HTML. What you receive is what the server decided you should see. Ask for a different page, and the whole cycle runs again.

Experience Cloud does not work like this, it uses a pattern known as 'Single Page Application' (SPA).

A useful way to picture this is a deck of cards. Navigation doesn't fetch a new deck, it just changes which card is face up. The frame around it never reloads, and what look like pages are really view states.

The part that analogy misses is that **the cards arrive blank.** The layout (the deck) comes down once. The data printed on each card is fetched on demand, by a separate request, at the moment a component needs it.

So when a visitor lands on your site, the sequence is roughly:

1. The browser requests the site and receives the application shell (layout, components, navigation logic).
2. Each component then makes its own call for the data it needs.
3. Those responses are rendered into the view.

Search, list views, record displays, CMS collections, navigation, all of these are components making an API call from the browser after the page has loaded.

![](https://storage.ghost.io/c/be/7b/be7ba71b-c876-4951-b5d8-a7a3ea20e990/content/images/2026/09/diagram-1-classic-vs-spa.svg)

The SPA pattern is a proven, widely adopted approach, and Experience Cloud is far from alone in using it. But it relocates something important. In the classic model, the page server decided what data left the building. In this model, that decision has moved to a set of endpoints that sit permanently exposed, waiting to be asked.

## Standard endpoints - the ones you didn't build

Salesforce provides a set of platform endpoints that power the out-of-the-box components. These endpoints are APIs that provide access to and in some cases edit data in Salesforce.

Some of these are formally documented by Salesforce as developer APIs. Others, notably the runtime endpoints an Aura site used to service its own components, are not. They are not secret, they are well known, widely written about in security research and entirely stable in practice. But Salesforce does not publish them as something you are expected to call directly from components, which means most teams building on the platform have no idea they exist.

You did not opt into any of them. They are part of the platform. If your site uses a standard record list, a search component, or a related list, it is calling these endpoints, and the response bodies include field names, values, display values, record IDs.

The important property of standard endpoints is that they run through the platform's user mode access control. Object permissions, field-level security and sharing all apply. The endpoint asks the platform "what is this user allowed to see?" and returns accordingly.

That is genuinely good news, with one condition attached; the answer depends entirely on how the requesting user's access is configured. The endpoint enforces your settings faithfully. If those settings are more generous than you intended, the endpoint will faithfully enforce that too.

## Custom endpoints - the ones you did build

Every `@AuraEnabled` Apex method in your org is an API endpoint, callable from any tool that can make http requests.

Developers rarely think of it this way. The method feels like part of a component, written to serve a specific part of a specific component. But the component and the method are separated by a network boundary, and the browser sits on the untrusted side of it.

Two consequences follow.

**First, the caller controls the parameters.** A method like `getContactsForAccount(accountId)` receives its account ID from the client. Whatever the UI intended to pass, the actual request can carry something else. If the method does not verify that the caller is entitled to that record, it will answer the question it was asked. This class of bug is not hypothetical or Salesforce-specific. One of two flaws in the 2025 McHire breach was an API that accepted an incremented ID with no ownership check, exposing applicant records at scale ([security researcher's notes](https://ian.sh/mcdonalds?ref=notes.hiddenbay.tech), [SecurityWeek](https://www.securityweek.com/mcdonalds-chatbot-recruitment-platform-leaked-64-million-job-applications/?ref=notes.hiddenbay.tech)).

**Second, Apex does not enforce permissions by default.** The class declaration governs sharing; `with sharing` respects record-level sharing rules, `without sharing` does not. But object and field-level permissions are a separate matter. Apex runs in system mode [(changed in recent apex versions - \*n.b.1)](#footnotes), and it will happily read fields the running user cannot see, unless the developer explicitly enforces user mode, through `WITH USER_MODE`, `Security.stripInaccessible()`, or equivalent.

*\* This default has changed. From the Summer '26 release (API version 67.0), Apex classes default to user mode for object and field-level permissions, but only where the class is on API v67.0 or later in its metadata. Classes left on older API versions keep the existing system mode behaviour, so a long tail of legacy code will continue to behave exactly as described above. In practice this makes the API version recorded against each exposed class a security-relevant detail, not just a housekeeping one.*

So a custom endpoint can, entirely by accident, become a path around the access control that the standard endpoints respect.

![](https://storage.ghost.io/c/be/7b/be7ba71b-c876-4951-b5d8-a7a3ea20e990/content/images/2026/09/diagram-2-endpoints-access-gate_1-1.svg)

## The browser is a convenience, not a gate

Because these endpoints are reached over standard HTTP, the browser is simply one client among many. Open the browser developer tools on any Experience Cloud site and the network tab shows the calls the page is making (full URLs, headers, payloads and responses). Every browser has a two-click option to copy any of those requests in a form that can be replayed from a terminal or an API client.

At that point the request is no longer coming from your site. It is coming from a script. Your carefully designed page, with its filtered list views and its conditionally rendered components, is no longer in the conversation. The visibility rules you applied in the page builder were never security controls; they were layout decisions. The endpoint does not know or care which component was supposed to call it.

![](https://storage.ghost.io/c/be/7b/be7ba71b-c876-4951-b5d8-a7a3ea20e990/content/images/2026/09/diagram-3-same-request-three-clients_1.svg)

And for a public Experience Cloud site, a meaningful portion of this surface is reachable before anyone has logged in at all.

## Why this matters

In 2026, Salesforce Trust published [advisories](https://status.salesforce.com/generalmessages/20000244?ref=hidden-bay-tech.ghost.io) concerning attackers querying CRM data directly from public Experience Cloud sites without authenticating. Salesforce's position was clear, this was not a platform vulnerability. It was customer configuration; guest user access granted beyond what the site actually needed to expose. For the fuller picture of who the Guest User actually represents, see the companion reference piece on [Experience Cloud user types](https://notes.hiddenbay.tech/experience-cloud-user-types/).

It's the combination of three things that makes this dangerous; an API surface that goes beyond what's on the page, no authentication needed to reach it, and configuration that's more open than intended. Putting those together, attackers have a way to extract your data, manually or at scale.

The organisations that have handled this well are the ones who moved past reviewing their site visually, and started asking what this user's access actually allows, and where custom code might be overriding it.

## What to actually do about it

The instinct, once this lands, is to go and audit the endpoints and try to lock them down.

That approach does not hold, and it is worth being clear about why. There is no definitive list. The standard endpoints change and multiply as Salesforce ships new features, some are undocumented, and every Apex method your team exposes adds another (which of course is within your control). Any inventory you build is accurate on the day you build it and decaying from the next release onwards.

So for the standard endpoints, the endpoints themselves are the wrong control point. The right one is the layer they all have to pass through, permissions and sharing, meaning object permissions, field-level security, sharing rules, and the rest of the platform's access settings. Every standard endpoint checks against that layer before it responds, and that layer decides the answer no matter which endpoint is asking, correct or not, including endpoints you've never heard of and endpoints that don't exist yet.

Custom endpoints need a second conversation, because they present a choice that the standard ones do not.

**They can defer to that same layer, and often they should.** An `@AuraEnabled` method declared `with sharing` and querying in user mode gives you exactly the property the standard endpoints have, which is that the access configuration becomes the single place the answer is decided. The catch is that this is a decision, not a default.

**Or they can bypass that layer, and sometimes that is legitimate.** An operation that genuinely has to read beyond what the caller can see for itself. There are valid reasons to run `without sharing` or in system mode, and pretending otherwise leads to worse code rather than safer code.

But be clear about what you have done when you do it. You have taken the platform's access control out of the path and replaced it with your own logic. That logic is now the security boundary. It does not merely need to be correct. It needs to be threat modelled, because the parameters arrive from the browser and the browser sits on the far side of the trust boundary. Every assumption the method makes about who is calling it, and what they are entitled to, has to be enforced inside the method itself. It's all very well custom code being super secure and 'bullet proof', but you still have to think of other entry points (the standard endpoints), so having bullet proof code does not mean that your configuration posture can 'relax', it should never be 'looser' than the custom code. I plan to write about this in the future.

So the Salesforce security layer (e.g. permissions and sharing [(\*n.b.2)](#footnotes)) is the control point for everything standard, and a deliberate, documented, audited decision for everything custom. Neither of those requires you to know the full inventory of standard endpoints.

Focussing your security assessment on what functionality you have built onto site pages can give a false sense of security. Knowing where the real security gate sits is what closes that gap.

If you take one thing from this, it should be that the attack surface of an Experience Cloud site is not what is rendered. It is the set of endpoints reachable from the browser, multiplied by the access granted to whoever is calling them, and for standard endpoints, that access is set in the salesforce security layer [(\*n.b.2)](#footnotes).

---

I provide fractional architect consulting around Experience Cloud. If you'd like to talk about this or any other Experience Cloud topic, please get in touch at [enquiries@hiddenbay.tech](mailto:enquiries@hiddenbay.tech)

---

Footnotes

*n.b.1:* This default is changing. From the Summer '26 release (API version 67.0), Apex classes default to user mode for object and field-level permissions, but only where the class is on API v67.0 or later in its metadata. Classes left on older API versions keep the existing system mode behaviour, so a long tail of legacy code will continue to behave exactly as described above. In practice this makes the API version recorded against each exposed class a security-relevant detail, not just a housekeeping one.

n.b.2 Typically this will be Permissions & Sharing, but may also include Experience Cloud security settings such as user visibility and feature specific settings like Sales Teams, Files, etc.

---