HTTP/1.1 + HTTP/2 client for Cloudflare Workers via cloudflare:sockets, bypassing cf-* header injection
npm install stealth-fetchHTTP/1.1 + HTTP/2 client for Cloudflare Workers built on cloudflare:sockets.
It avoids automatic cf-* header injection by using raw TCP sockets.
- HTTP/1.1 + HTTP/2 with ALPN negotiation
- WASM TLS (rustls) for TLS 1.2/1.3 + ALPN control
- HTTP/2 connection pooling and protocol cache
- NAT64 fallback for blocked outbound connections
- Redirects, retries, and timeouts
- Raw headers preserved (order + multi-value)
``bash`
pnpm add stealth-fetch
`typescript
import { request } from "stealth-fetch";
const response = await request("https://api.example.com/v1/data", {
method: "POST",
headers: {
Authorization: "Bearer sk-...",
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "gpt-4",
messages: [{ role: "user", content: "Hello" }],
}),
});
const data = await response.json();
console.log(data);
`
If you need a standard Web Response object (with bodyUsed, clone, text,json, etc.), convert with toWebResponse():
`typescript
import { request, toWebResponse } from "stealth-fetch";
const resp = await request("https://httpbin.org/headers", { protocol: "h2" });
const webResp = toWebResponse(resp);
const text = await webResp.text();
`
Note: don’t call resp.text()/json()/arrayBuffer() before converting, or the
body stream will already be consumed.
If you need a pre-cloned pair (using ReadableStream.tee()), pass{ tee: true }:
`typescript`
const { response, clone } = toWebResponse(resp, { tee: true });
Returns Promise.
Options
| Option | Type | Default | Description |
| ---------------- | ------------------------------------------------------------ | ---------- | ---------------------------------------------------------------------------------------------------------------------- |
| method | string | 'GET' | HTTP method |headers
| | Record | {} | Request headers |body
| | string \| Uint8Array \| ReadableStream | null | Request body |protocol
| | 'h2' \| 'http/1.1' \| 'auto' | 'auto' | Protocol selection |timeout
| | number | 30000 | Overall timeout from call until response headers (includes retries/redirects) |headersTimeout
| | number | — | Timeout waiting for response headers |bodyTimeout
| | number | — | Idle timeout waiting for response body data |signal
| | AbortSignal | — | Cancellation signal |redirect
| | 'follow' \| 'manual' | 'follow' | Redirect handling |maxRedirects
| | number | 5 | Max redirects to follow |decompress
| | boolean | true | Auto-decompress gzip/deflate responses |compressBody
| | boolean | false | Gzip-compress request body (Uint8Array > 1KB) |strategy
| | 'compat' \| 'fast-h1' | 'compat' | compat: ALPN + protocol cache (h2 supported); fast-h1: platform TLS for non-CF, WASM TLS h1 for CF (faster, no h2) |
Response
`typescript
interface HttpResponse {
status: number;
statusText: string;
headers: Record
rawHeaders: ReadonlyArray<[string, string]>;
protocol: "h2" | "http/1.1";
body: ReadableStream
text(): Promise
json(): Promise
arrayBuffer(): Promise
getSetCookie(): string[];
}
`
`typescript`
import {
Http2Client,
Http2Connection,
http1Request,
clearPool,
preconnect,
createWasmTLSSocket,
} from "stealth-fetch";
- Http2Client — HTTP/2 client with stream multiplexingHttp2Connection
- — Low-level HTTP/2 connectionhttp1Request(socket, request)
- — HTTP/1.1 over a raw socketclearPool()
- — Clear the HTTP/2 connection poolpreconnect(hostname, port?)
- — Pre-establish an HTTP/2 connectioncreateWasmTLSSocket(hostname, port, alpnList)
- — WASM TLS socket with ALPN
- fetch injects cf-* headers in Workers, this library does not.fetch
- exposes standard Request/Response objects; this library returns aHttpResponse
custom (use toWebResponse() if you need a Web Response).h1
- Protocol control (force /h2, ALPN, NAT64) is supported here.
NAT64 is a best-effort fallback. Public NAT64 gateways can be unstable or
blocked depending on region and routing, so some connections may fail. If you
rely on NAT64, plan for retries or fallback behavior in your application.
- Cloudflare Workers runtime
- nodejs_compat compatibility flag
The repo includes a minimal worker at examples/worker.ts with endpoints:
- /http1/http2
- /auto
- /fetch
- /single?url=&mode=auto|h1|h2|fetch
-
wrangler.toml points to examples/worker.ts as the entry.
`bash`
pnpm dev # Local dev server (wrangler)
pnpm test:run # Run tests once
pnpm test # Run tests in watch mode
pnpm type-check # TypeScript type check
pnpm build # Build to dist/
pnpm lint # ESLint
pnpm format # Prettier
See CONTRIBUTING.md for commit message rules and dev notes.
Requires Rust toolchain with wasm-pack and wasm32-unknown-unknown target:
`bash``
pnpm build:wasm