Quick start
Your first request, and the four calls every program makes.
Every program follows the same shape:
initTLS()once, before any request. It loads the native library.new Session(options)with the browser profile to impersonate.session.get()/post()/ … as many times as you like.session.close(), thendestroyTLS()at shutdown.
import {
initTLS,
destroyTLS,
Session,
ClientIdentifier,
} from "node-tls-client";
await initTLS(); // loads (and on first run downloads) the native library
const session = new Session({
clientIdentifier: ClientIdentifier.chrome_150,
headers: {
"user-agent":
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/150.0.0.0 Safari/537.36",
},
timeout: 10_000, // ms, for every request of this session
});
try {
// tls.peet.ws echoes back the fingerprint it saw
const res = await session.get("https://tls.peet.ws/api/all");
const seen = await res.json();
console.log(res.status, res.usedProtocol); // 200 HTTP/2.0
console.log(seen.tls.ja4); // t13d1516h2_8daaf6152771_…, Chrome's JA4
} finally {
await session.close();
await destroyTLS();
}tls.peet.ws replies with the fingerprint it saw. Swap in your own URL once you've seen it work.
clientIdentifier, headers and timeout are only a few of the session's settings. Every option new Session() accepts is listed in SessionOptions; per-request options are in Request options.
Set the User-Agent yourself
A profile copies the browser's TLS and HTTP/2 handshake, not its headers.
Without a headers option, every request is sent with User-Agent: tls-client/2.2.0, which no browser sends. Set a user-agent that matches the
profile, as above. See Headers.
One-off requests
fetch() sends a single request in a throwaway session. It's handy when every request needs different settings:
// One request in a throwaway session: its own profile and timeout,
// closed as soon as the response arrives.
const res = await fetch("https://httpbin.org/post", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ hello: "world" }),
options: { clientIdentifier: ClientIdentifier.firefox_148, timeout: 5000 },
});
const echoed = await res.json();
console.log(res.status, echoed.json); // 200 { hello: 'world' }For several requests to the same site, prefer a Session. It keeps cookies and reuses connections; fetch() starts from scratch every time.
Things that surprise people
- Redirects aren't followed unless you pass
followRedirects: true. You get the3xxresponse instead. - A failed request doesn't throw. DNS errors, TLS errors, proxy errors and timeouts resolve with
status: 0, andbodyholds the reason. Checkres.okorres.status. See Errors and debugging. - Header values are arrays:
res.headers["Content-Type"]is["application/json"]. - The timeout is per session (
timeout, in milliseconds), not per request.