# Network
URL: /remote-components/docs/network

Remotes call public APIs with plain fetch. Your page decides how much network they get.

> For AI agents: a documentation index is available at [llms.txt](/llms.txt). Use `.md` for canonical markdown pages; `.mdx` is kept as a backwards-compatible alias on supported URL paths.

## 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](https://open-meteo.com), which needs no key.

\[interactive preview omitted]

```
<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"]} />
```

widgets/weather.html

```html
<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&current=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.

> [!warn]
>
> 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](/remote-components/docs/security).