You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 930879f
Browse filesBrowse the repository at this point in the historyBrowse files
feat(fetch): pin *.localhost connects to loopback (IPv4 by default)
createFetch now points the *.localhost node:http path straight at the
loopback interface instead of resolving bare localhost, taking DNS out of
the loopback hop. localhost commonly resolves to IPv6 ::1 first while many
local dev ingresses (kind, Docker port publishing) listen on IPv4 only, so
the old DNS-driven connect hit ::1 and failed without Happy-Eyeballs
fallback. The original Host header is still preserved for subdomain routing.
Adds createFetch({ loopback }) — '127.0.0.1' (default) | '::1' | false
(pre-1.2 DNS behavior).
Copy file name to clipboardExpand all lines: packages/fetch/README.md
+19-4Lines changed: 19 additions & 4 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -4,12 +4,13 @@ Isomorphic fetch that resolves `*.localhost` subdomains and preserves `Host` hea
4
4
5
5
## Why
6
6
7
-
Node.js has two issues with `*.localhost` subdomains:
7
+
Node.js has three issues with `*.localhost` subdomains:
8
8
9
9
1.**DNS** — `fetch('http://auth.localhost:3000/')` throws `ENOTFOUND` because Node (undici) doesn't resolve `*.localhost` to loopback ([nodejs/node#50871](https://github.com/nodejs/node/issues/50871)).
10
10
2.**Host header** — Node's fetch treats `Host` as a forbidden header and silently drops it, breaking server-side subdomain routing.
11
+
3.**Loopback family** — `localhost` commonly resolves to IPv6 `::1` first, but many local dev ingresses (kind, Docker's port publishing) listen on IPv4 only, so a DNS-driven connect reaches `::1` and fails on setups without Node's Happy-Eyeballs fallback.
11
12
12
-
Browsers handle both correctly. This package fixes both in Node by using `node:http`/`node:https` for `*.localhost` URLs and passing everything else through to `globalThis.fetch`.
13
+
Browsers handle all three correctly. This package fixes them in Node by using `node:http`/`node:https` for `*.localhost` URLs — pinning the connect to the loopback interface (IPv4 by default) while preserving the original `Host` header — and passing everything else through to `globalThis.fetch`.
13
14
14
15
## Install
15
16
@@ -34,9 +35,23 @@ const res = await fetch('http://auth.localhost:3000/graphql', {
Returns a fetch function. In Node.js, `*.localhost` URLs are handled via `node:http`/`node:https`. Everything else delegates to `globalThis.fetch`. The result is cached.
40
+
Returns a fetch function. In Node.js, `*.localhost` URLs are handled via `node:http`/`node:https`. Everything else delegates to `globalThis.fetch`. The default-configuration result is cached.
41
+
42
+
**`CreateFetchOptions`**
43
+
44
+
-`loopback?: '127.0.0.1' | '::1' | false` — how to reach the loopback interface for `*.localhost` URLs (Node only). The connect is pinned to this address, removing DNS from the loopback hop; the original `Host` header is preserved so subdomain routing still works.
45
+
-`'127.0.0.1'` (default) — pin the IPv4 loopback.
46
+
-`'::1'` — pin the IPv6 loopback.
47
+
-`false` — no pin; rewrite the host to `localhost` and rely on system DNS (the pre-1.2 behavior).
48
+
49
+
Ignored in browsers, which resolve `*.localhost` natively.
0 commit comments