v154 · network · local network access

targetAddressSpace for WebSockets

A page on https:// cannot open a ws:// connection — that is mixed content, and it is the rule that stops a hijacked page talking to your router. It also stops a legitimate web app talking to a printer, a 3D printer, or a dev server on the same desk. targetAddressSpace is the declared, permission-gated exception, and Chrome 154 brings it to WebSockets to match fetch().

concepts

  1. Does this browser read it?

    The option lives in the WebSocketInit dictionary, so it can be probed with a getter — no version sniffing, no connection required. The demo also checks the accepted values and shows what an invalid one does.

  2. What counts as local

    Loopback, private, and public are three different address spaces with three different rules, and which one a hostname falls into is not always obvious. Classify a set of addresses and see which connections need the option at all.

  3. Opening one

    A real connection to this origin's echo endpoint, opened with and without the option, side by side. On a same-origin ws:// from http://localhost nothing is blocked — which is itself the useful lesson about when the option is and is not needed.

why it shipped

Local Network Access exists because a public web page reaching a device on your home network is a genuine attack, and the browser cannot tell a malicious page from a helpful one by looking at the request. So it asks: a page that declares it is deliberately targeting a local address gets a permission prompt, and the user decides.

fetch() has had targetAddressSpace for a while; WebSockets did not, which left long-lived connections to local devices — the transport those devices actually want — with no escape hatch at all. Declaring the intent up front is what makes the prompt meaningful: the browser can say which local address the page wants, rather than asking about a request the user cannot see.

the API

// Only expressible because the options bag exists.
const socket = new WebSocket("ws://printer.local:9100/stream", {
  protocols: ["escpos"],
  targetAddressSpace: "local",
});

// "loopback" is the narrower case: 127.0.0.1 and ::1 only.
new WebSocket("ws://127.0.0.1:8000/hmr", { targetAddressSpace: "loopback" });

The option is a declaration, not a bypass: it tells the browser what to ask the user about. A page that lies about the address space does not get a connection, it gets a failed one.

references