workers-unsafe-https
v0.2.0
Published
HTTPS requests from Cloudflare Workers to servers with self-signed certificates
Maintainers
Readme
workers-unsafe-https
unsafeFetch() lets a Cloudflare Worker make an HTTPS request to an origin
whose certificate is self-signed or otherwise untrusted.
Cloudflare's built-in fetch(), node:https, node:tls, and
cloudflare:sockets TLS mode always validate the server certificate and do not
support rejectUnauthorized: false. This package opens a raw Workers TCP socket
and runs TLS 1.2/1.3 in JavaScript before speaking HTTP/1.1.
Security warning
This package deliberately disables server certificate verification. Traffic is encrypted, but the server is not authenticated, so a man-in-the-middle attacker can impersonate it. Prefer a publicly trusted certificate, a private CA that the platform trusts, Cloudflare Tunnel, or Workers VPC when possible.
Install
yarn add workers-unsafe-httpsUse a current compatibility date. nodejs_compat is enabled by default for
compatibility dates on or after 2026-08-04. For an older date, add the flag
explicitly because the TLS dependency imports Node compatibility modules:
{
"compatibility_date": "2026-08-11"
}Usage
import { unsafeFetch } from "workers-unsafe-https";
export default {
async fetch(): Promise<Response> {
const response = await unsafeFetch("https://internal.example:8443/status", {
headers: { authorization: "Bearer secret" },
connectTimeout: 10_000,
headersTimeout: 30_000,
});
return new Response(response.body, response);
},
} satisfies ExportedHandler;The return value is a standard Response. Its body is streamed from the TCP
socket and closing or cancelling it closes the underlying connection.
Raw TLS connection
Use the named unsafeTlsConnect() export when you need TLS without HTTP. Its
streams contain plaintext application data; TLS records remain internal to the
package.
import { unsafeTlsConnect } from "workers-unsafe-https";
const connection = await unsafeTlsConnect(
{ hostname: "internal.example", port: 8443 },
{ connectTimeout: 10_000 },
);
const writer = connection.writable.getWriter();
await writer.write(new TextEncoder().encode("application protocol bytes"));
writer.releaseLock();
const reader = connection.readable.getReader();
const { value } = await reader.read();
console.log(value, connection.metadata.version);
reader.releaseLock();
await connection.close();
await connection.closed;unsafeTlsConnect() returns:
readable: decrypted bytes received from the server.writable: plaintext bytes to encrypt and send to the server.metadata: negotiated TLS version, cipher suite, key type, and ALPN value.closed: resolves after the underlying TCP connection closes.close(): idempotently closes TLS and TCP resources.
Supported
- HTTPS URLs over Workers TCP sockets
- TLS 1.2 and TLS 1.3
- Raw plaintext streams through
unsafeTlsConnect() - HTTP/1.1 request methods and headers
- String,
URLSearchParams,Blob,ArrayBuffer, and typed-array request bodies Content-Length, chunked, and connection-delimited response bodies- Streaming response bodies
- Bounded buffering and backpressure between the TCP socket and response reader
AbortSignal, TCP/TLS timeout, and response-header timeout
Limitations
- Certificate verification is always disabled by design.
- HTTP/1.1 only; no HTTP/2 or HTTP/3.
- One TCP/TLS connection per request; no keep-alive pool.
- Redirects are returned to the caller and are not followed automatically.
connectTimeoutcovers both the TCP connection and TLS handshake. There is no built-in whole-body timeout; useAbortSignalwhen one is required.- Response compression is disabled with
Accept-Encoding: identityand is not decoded by the package. - Streaming request bodies,
FormData, proxies, WebSocket upgrades, and HTTP trailers are not supported. - Cloudflare's normal TCP restrictions still apply, including production access restrictions for localhost/private addresses unless an appropriate network capability such as Workers VPC is configured.
Development
yarn install
yarn check
yarn testyarn test generates a self-signed certificate, starts a real local HTTPS
server, bundles the Worker, runs it in workerd, verifies that native fetch()
rejects the certificate, and verifies that unsafeFetch() handles fixed-length
and chunked responses.
