Remotes call public APIs with plain fetch. Your page decides how much network they get.
Public by default
A remote may reach any https: or wss: endpoint. Requests carry none of your page's cookies. The remote runs on its own origin, or on an opaque one with no cookies at all. CORS applies as usual.
Pick a policy below and click Get weather. The data comes from Open-Meteo, which needs no key.
<RemoteComponent src="https://widgets.example/weather.html" />
<RemoteComponent src="https://widgets.example/weather.html" network="none" />
<RemoteComponent src="https://widgets.example/weather.html" network={["https://api.open-meteo.com"]} />
<RemoteComponent src="https://widgets.example/weather.html" network={["https://example.com"]} /><div style="display: grid; gap: 8px">
<div class="row" style="justify-content: space-between">
<span style="font-weight: 500">Berlin</span>
<span id="reading" style="font-variant-numeric: tabular-nums"></span>
</div>
<div id="error" style="color: var(--color-danger)" hidden></div>
<div class="row"><button id="load">Get weather</button></div>
</div>
<script>
const FORECAST = "https://api.open-meteo.com/v1/forecast?latitude=52.52&longitude=13.41¤t=temperature_2m,wind_speed_10m";
const button = document.querySelector("#load");
const reading = document.querySelector("#reading");
const error = document.querySelector("#error");
async function load() {
button.disabled = true;
button.textContent = "Loading…";
error.hidden = true;
try {
const res = await fetch(FORECAST);
if (!res.ok) throw new Error(`Open-Meteo answered ${res.status}`);
const { current } = await res.json();
reading.textContent = `${current.temperature_2m.toFixed(1)}°C, wind ${current.wind_speed_10m.toFixed(0)} km/h`;
} catch (e) {
reading.textContent = "";
error.textContent = navigator.onLine ? `Blocked: ${e.message}` : "You are offline.";
error.hidden = false;
}
button.disabled = false;
button.textContent = "Refresh";
}
button.onclick = load;
// Retry by itself when the connection comes back.
addEventListener("online", () => {
if (!error.hidden) void load();
});
</script>Network policy
network | connect-src | |
|---|---|---|
"public" | https: wss: | Default. Any public endpoint. |
"none" | 'none' | No network at all. |
["https://api.example.com"] | those origins | Exactly these. https://*.example.com matches subdomains. |
Set it per element with network, or for every remote on <RemoteHostProvider network>. It is a Content Security Policy. For documents your page shows itself (the html prop, Safe Content Frame) the host writes it into the document. For URL remotes it goes on the iframe's csp attribute, which the serving layer accepts.
Warning
Only Chromium enforces the iframe csp attribute. In Firefox and Safari, a URL remote gets the policy its own server sends. To be sure everywhere, show the document yourself with the html prop or isolation="scf".
What a page needs
A page can narrow its own network with a Content Security Policy of its own. The browser enforces every policy on the document, so the page's meta tag can take origins away from the host's policy but never add one.
<meta http-equiv="Content-Security-Policy" content="connect-src https://api.open-meteo.com">Your data and the user's authority
There is no relay through the host, and the remote never gets your credentials. When it needs your data or acts for the user, pass a prop or a function. You implement it and check it like any API.
// The remote never sees the token. Your code decides what it may ask for.
<RemoteComponent
src="https://widgets.example/orders.html"
loadOrders={async (status: string) => api.orders({ status, user: session.userId })}
/>What to trust it with
With network on, a remote can send anything it can read to any public endpoint: its props, what it stores, what the user types. Treat it like a third-party script. Only pass data you would give that publisher. For code you don't trust with data, such as widgets from strangers or a model's output, use network="none".
CSP can't express address ranges. https: also matches private and loopback addresses. Those requests carry none of your cookies, and Chromium blocks public frames from private addresses without a preflight. If that is not enough, use "none" or a list. See Security.