{"_id":"tls-impersonate","_rev":"5-4d4500f32b51559d309daa10dd8dd94c","name":"tls-impersonate","dist-tags":{"latest":"0.2.1"},"versions":{"0.0.1":{"name":"tls-impersonate","version":"0.0.1","keywords":["TLS","fingerprint","impersonate","openssl","extensions","control"],"author":{"name":"Tim Perry","email":"tim@httptoolkit.com"},"license":"Apache-2.0","_id":"tls-impersonate@0.0.1","maintainers":[{"name":"pimterry","email":"pimterry@gmail.com"}],"homepage":"https://github.com/httptoolkit/tls-impersonate#readme","bugs":{"url":"https://github.com/httptoolkit/tls-impersonate/issues"},"os":["linux"],"dist":{"shasum":"b94fb05ba7d9f6a118f002311e6da8da8ea88fdf","tarball":"https://registry.npmjs.org/tls-impersonate/-/tls-impersonate-0.0.1.tgz","fileCount":13,"integrity":"sha512-ZxQ02uftpg0nb9zTvBw0kk/9fhf+l78ClRXfrZKyoZUUOE0jMhRbjueNYK178UAlYR0+3qdaui5f3wWSJCmwZw==","signatures":[{"sig":"MEQCIHQQvUtPCaEvorLMj/g+y0kVdExd0N5hDfb0CDe/SIN5AiAFpE5UKkQJOZdUcayUv1bKTtp9mWNiDN400mBlI6tEWg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":83683},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","engines":{"node":">=24.15.0"},"gitHead":"1305eab3091bf12e9b9875009c661c8809ca601d","gypfile":true,"scripts":{"test":"TS_NODE_TRANSPILE_ONLY=true mocha","build":"node-gyp rebuild && tsc","install":"node-gyp-build","prepack":"test -n \"$KEEP_PREBUILDS\" || rm -rf prebuilds","build:ts":"tsc","prepublishOnly":"tsc"},"ts-node":{"esm":true,"project":"tsconfig.test.json","transpileOnly":true},"_npmUser":{"name":"pimterry","email":"pimterry@gmail.com"},"repository":{"url":"git+https://github.com/httptoolkit/tls-impersonate.git","type":"git"},"_npmVersion":"11.16.0","description":"TLS fingerprint control within Node.js's normal networking APIs ","directories":{},"_nodeVersion":"24.15.0","dependencies":{"node-gyp-build":"^4.8.4"},"_hasShrinkwrap":false,"devDependencies":{"chai":"^6.2.2","mocha":"^11.7.5","semver":"^7.7.4","ts-node":"^10.9.2","typescript":"^5.9.3","@types/chai":"^5.2.3","@types/node":"^25.3.3","@types/mocha":"^10.0.10","@types/semver":"^7.7.1","read-tls-client-hello":"^2.0.0"},"_npmOperationalInternal":{"tmp":"tmp/tls-impersonate_0.0.1_1783068750787_0.9908066639932602","host":"s3://npm-registry-packages-npm-production"}},"0.0.2":{"name":"tls-impersonate","version":"0.0.2","keywords":["TLS","fingerprint","impersonate","openssl","extensions","control"],"author":{"name":"Tim Perry","email":"tim@httptoolkit.com"},"license":"Apache-2.0","_id":"tls-impersonate@0.0.2","maintainers":[{"name":"pimterry","email":"pimterry@gmail.com"}],"homepage":"https://github.com/httptoolkit/node-tls-impersonate#readme","bugs":{"url":"https://github.com/httptoolkit/node-tls-impersonate/issues"},"os":["linux","darwin","win32"],"dist":{"shasum":"b44e8f3e99c0331e6c9a475cdd12ea2c747f781d","tarball":"https://registry.npmjs.org/tls-impersonate/-/tls-impersonate-0.0.2.tgz","fileCount":18,"integrity":"sha512-SiFGnxBHf/z9+LoCOoHmXCEZw/ba7hF41TOH9qt2bQIPudILPwDJgpNDzY7hl4prfnJ1cyTyy1coxu2eVMD6QQ==","signatures":[{"sig":"MEUCIEXeM2EtB3YNZ/P522mLS9tppSWHYlZ0CNzrjNpnmPw2AiEAsy6TJ0GCfXTDcUXjgIaPGWpzaJWMz6ptlrCHqZnd3bw=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/tls-impersonate@0.0.2","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":390838},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","engines":{"node":">=24.15.0"},"gitHead":"75f6888027545cf5cb92b05d60a6418209621026","gypfile":true,"scripts":{"test":"TS_NODE_TRANSPILE_ONLY=true mocha","build":"node-gyp rebuild && tsc","install":"node-gyp-build","build:ts":"tsc","prebuild":"prebuildify --napi --strip --tag-libc","test:smoke":"node scripts/smoke-prebuild.cjs","prepublishOnly":"tsc"},"ts-node":{"esm":true,"project":"tsconfig.test.json","transpileOnly":true},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","approver":{"name":"pimterry","email":"pimterry@gmail.com"},"trustedPublisher":{"id":"github","oidcConfigId":"oidc:288d56c3-335a-49d2-9b3a-ebcc068cea66"}},"repository":{"url":"git+https://github.com/httptoolkit/node-tls-impersonate.git","type":"git"},"_npmVersion":"11.18.0","description":"TLS fingerprint control within Node.js's normal networking APIs ","directories":{},"_nodeVersion":"24.18.0","dependencies":{"node-gyp-build":"^4.8.4"},"_hasShrinkwrap":false,"devDependencies":{"chai":"^6.2.2","mocha":"^11.7.5","semver":"^7.7.4","ts-node":"^10.9.2","node-gyp":"^13.0.0","typescript":"^5.9.3","@types/chai":"^5.2.3","@types/node":"^25.3.3","prebuildify":"^6.0.1","@types/mocha":"^10.0.10","@types/semver":"^7.7.1","read-tls-client-hello":"^2.0.0"},"_npmOperationalInternal":{"tmp":"tmp/tls-impersonate_0.0.2_1783512977574_0.33978534820541584","host":"s3://npm-registry-packages-npm-production"}},"0.1.0":{"name":"tls-impersonate","version":"0.1.0","keywords":["TLS","fingerprint","impersonate","openssl","extensions","control"],"author":{"name":"Tim Perry","email":"tim@httptoolkit.com"},"license":"Apache-2.0","_id":"tls-impersonate@0.1.0","maintainers":[{"name":"pimterry","email":"pimterry@gmail.com"}],"homepage":"https://github.com/httptoolkit/node-tls-impersonate#readme","bugs":{"url":"https://github.com/httptoolkit/node-tls-impersonate/issues"},"os":["linux","darwin","win32"],"dist":{"shasum":"fd3f71c185226dcfd0d211c65a2d9375acc47fa6","tarball":"https://registry.npmjs.org/tls-impersonate/-/tls-impersonate-0.1.0.tgz","fileCount":18,"integrity":"sha512-fV5LFIpVmJXkUJiDk1AT9qHMlwVXT7D8Nqs8hhMl9l6I4SgL+jPLRPtV95KNAT3A0ECzritAtUPShhXWwWzoUw==","signatures":[{"sig":"MEUCIQDJ3//i6rk7hXuNjZG/iqx8eivx8nXOCNM5r9PJ+BWDWAIgVfIvK0WyMQQhGrzFDmjRg0QSrO++OQ4jWfOxq2nxR9A=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/tls-impersonate@0.1.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":440047},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","engines":{"node":">=24.15.0"},"gitHead":"e39d178ba5705a95e2edf67d0b4f994609d56e43","gypfile":true,"scripts":{"test":"TS_NODE_TRANSPILE_ONLY=true mocha","build":"node-gyp rebuild && tsc","install":"node-gyp-build","build:ts":"tsc","prebuild":"prebuildify --napi --strip --tag-libc","test:smoke":"node scripts/smoke-prebuild.cjs","prepublishOnly":"tsc"},"ts-node":{"esm":true,"project":"tsconfig.test.json","transpileOnly":true},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","approver":{"name":"pimterry","email":"pimterry@gmail.com"},"trustedPublisher":{"id":"github","oidcConfigId":"oidc:288d56c3-335a-49d2-9b3a-ebcc068cea66"}},"repository":{"url":"git+https://github.com/httptoolkit/node-tls-impersonate.git","type":"git"},"_npmVersion":"11.18.0","description":"TLS fingerprint control within Node.js's normal networking APIs ","directories":{},"_nodeVersion":"24.18.0","dependencies":{"node-gyp-build":"^4.8.4"},"_hasShrinkwrap":false,"devDependencies":{"chai":"^6.2.2","mocha":"^11.7.5","semver":"^7.7.4","ts-node":"^10.9.2","node-gyp":"^13.0.0","typescript":"^5.9.3","@types/chai":"^5.2.3","@types/node":"^25.3.3","prebuildify":"^6.0.1","@types/mocha":"^10.0.10","@types/semver":"^7.7.1","read-tls-client-hello":"^2.0.0"},"_npmOperationalInternal":{"tmp":"tmp/tls-impersonate_0.1.0_1783609304456_0.16349387311356267","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"tls-impersonate","version":"0.2.0","keywords":["TLS","fingerprint","impersonate","openssl","extensions","control"],"author":{"name":"Tim Perry","email":"tim@httptoolkit.com"},"license":"Apache-2.0","_id":"tls-impersonate@0.2.0","maintainers":[{"name":"pimterry","email":"pimterry@gmail.com"}],"homepage":"https://github.com/httptoolkit/node-tls-impersonate#readme","bugs":{"url":"https://github.com/httptoolkit/node-tls-impersonate/issues"},"os":["linux","darwin","win32"],"dist":{"shasum":"adb1bb0f54e1f099668b809b6646b971815de0ec","tarball":"https://registry.npmjs.org/tls-impersonate/-/tls-impersonate-0.2.0.tgz","fileCount":18,"integrity":"sha512-kHiRPk7sf13gfexNg7goo+vG5J2zkopKsSH/fJZ2RfDpNy2p34LfXui4rrwTZeRGyylHGW/vfYLh/lJYUWKGKg==","signatures":[{"sig":"MEUCIQDOz66Tv2FS1RQCtF8XIaCKGTb/XlheroYRPJnBlmBZbwIgATwxPI5JtCg1+WQownfN8Pz7w46qSuwcmFpo7P6k9ZM=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/tls-impersonate@0.2.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":452832},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","engines":{"node":">=24.15.0"},"gitHead":"2558dc5ba08440150f603f6939badea8db7df1a9","gypfile":true,"scripts":{"test":"TS_NODE_TRANSPILE_ONLY=true mocha","build":"node-gyp rebuild && tsc","install":"node-gyp-build","build:ts":"tsc","prebuild":"prebuildify --napi --strip --tag-libc","test:smoke":"node scripts/smoke-prebuild.cjs","prepublishOnly":"tsc"},"ts-node":{"esm":true,"project":"tsconfig.test.json","transpileOnly":true},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","approver":{"name":"pimterry","email":"pimterry@gmail.com"},"trustedPublisher":{"id":"github","oidcConfigId":"oidc:288d56c3-335a-49d2-9b3a-ebcc068cea66"}},"repository":{"url":"git+https://github.com/httptoolkit/node-tls-impersonate.git","type":"git"},"_npmVersion":"11.19.1","description":"TLS fingerprint control within Node.js's normal networking APIs ","directories":{},"_nodeVersion":"24.20.0","dependencies":{"node-gyp-build":"^4.8.4"},"_hasShrinkwrap":false,"devDependencies":{"chai":"^6.2.2","mocha":"^11.7.5","semver":"^7.7.4","ts-node":"^10.9.2","node-gyp":"^13.0.0","typescript":"^5.9.3","@types/chai":"^5.2.3","@types/node":"^25.3.3","prebuildify":"^6.0.1","@types/mocha":"^10.0.10","@types/semver":"^7.7.1","read-tls-client-hello":"^2.0.0"},"_npmOperationalInternal":{"tmp":"tmp/tls-impersonate_0.2.0_1789548958623_0.6326411932836913","host":"s3://npm-registry-packages-npm-production"}},"0.2.1":{"os":["linux","darwin","win32"],"_id":"tls-impersonate@0.2.1","bugs":{"url":"https://github.com/httptoolkit/node-tls-impersonate/issues"},"dist":{"shasum":"4b5532a195df776464704b2da0696ae3ac7deb16","tarball":"https://registry.npmjs.org/tls-impersonate/-/tls-impersonate-0.2.1.tgz","integrity":"sha512-Co207w6PAoAf5lbCA3+G/unhjAkY594/W6dLbB5qlBYJdPmb3kB6tDbvPX3ejNRFr7Rtv7gGkc9ls+/hwh2acw==","fileCount":18,"unpackedSize":435306,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/tls-impersonate@0.2.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIH/2fw9ecWqSLxsGR22cnrDCNtHwESYOC+xum0cPRvgQAiEA4JVZesoRZI8QlgIf9++crxxvmTLVYBAEzhv6pAp2RY0="}]},"main":"dist/index.js","name":"tls-impersonate","type":"module","types":"dist/index.d.ts","author":{"name":"Tim Perry","email":"tim@httptoolkit.com"},"engines":{"node":">=24.15.0"},"gitHead":"ca865d7229d90d6dcbc7f4d298e2000c646a51bc","gypfile":true,"license":"Apache-2.0","scripts":{"test":"TS_NODE_TRANSPILE_ONLY=true mocha","build":"node-gyp rebuild && tsc","install":"node-gyp-build","build:ts":"tsc","prebuild":"prebuildify --napi --strip --tag-libc","test:smoke":"node scripts/smoke-prebuild.cjs","prepublishOnly":"tsc"},"ts-node":{"esm":true,"project":"tsconfig.test.json","transpileOnly":true},"version":"0.2.1","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"288d56c3-335a-49d2-9b3a-ebcc068cea66"},"approver":{"name":"pimterry","email":"pimterry@gmail.com"}},"homepage":"https://github.com/httptoolkit/node-tls-impersonate#readme","keywords":["TLS","fingerprint","impersonate","openssl","extensions","control"],"repository":{"url":"git+https://github.com/httptoolkit/node-tls-impersonate.git","type":"git"},"_npmVersion":"11.21.0","description":"TLS fingerprint control within Node.js's normal networking APIs ","directories":{},"maintainers":[{"name":"pimterry","email":"pimterry@gmail.com"}],"_nodeVersion":"24.21.0","dependencies":{"node-gyp-build":"^4.8.4"},"devDependencies":{"chai":"^6.2.2","mocha":"^11.7.5","semver":"^7.7.4","ts-node":"^10.9.2","node-gyp":"^13.0.0","typescript":"^5.9.3","@types/chai":"^5.2.3","@types/node":"^25.3.3","prebuildify":"^6.0.1","@types/mocha":"^10.0.10","@types/semver":"^7.7.1","read-tls-client-hello":"^2.0.0"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/tls-impersonate_0.2.1_1791962716008_0.3172788582345112"},"_hasShrinkwrap":false}},"time":{"created":"2026-07-03T08:52:30.730Z","modified":"2026-10-14T07:25:16.373Z","0.0.1":"2026-07-03T08:52:30.915Z","0.0.2":"2026-07-08T12:16:17.659Z","0.1.0":"2026-07-09T15:01:44.623Z","0.2.0":"2026-09-16T08:55:58.706Z","0.2.1":"2026-10-14T07:25:16.116Z"},"bugs":{"url":"https://github.com/httptoolkit/node-tls-impersonate/issues"},"author":{"name":"Tim Perry","email":"tim@httptoolkit.com"},"license":"Apache-2.0","homepage":"https://github.com/httptoolkit/node-tls-impersonate#readme","keywords":["TLS","fingerprint","impersonate","openssl","extensions","control"],"repository":{"url":"git+https://github.com/httptoolkit/node-tls-impersonate.git","type":"git"},"description":"TLS fingerprint control within Node.js's normal networking APIs ","maintainers":[{"name":"pimterry","email":"pimterry@gmail.com"}],"readme":"# TLS Impersonate [![Build Status](https://github.com/httptoolkit/node-tls-impersonate/workflows/CI/badge.svg)](https://github.com/httptoolkit/node-tls-impersonate/actions) [![Available on NPM](https://img.shields.io/npm/v/tls-impersonate.svg)](https://npmjs.com/package/tls-impersonate)\n\n> _Part of [HTTP Toolkit](https://httptoolkit.com): powerful tools for building, testing & debugging HTTP(S)_\n\nReproduce arbitrary TLS ClientHello fingerprints (JA3/JA4) while using Node.js's normal networking APIs.\n\nServers and anti-bot systems increasingly fingerprint the exact shape of a TLS ClientHello - its cipher suites, extensions, supported groups and their ordering - to tell real browsers apart from other clients. Node.js sends its own recognisable fingerprint, and limited options to change it.\n\nThis library takes a target ClientHello specification and builds a standard [`tls.SecureContext`](https://nodejs.org/api/tls.html#tlscreatesecurecontextoptions) which reproduces that fingerprint as closely as possible. The result plugs straight into `tls.connect`, `https`, or any Node API that accepts a secure context, and can perfectly match Chrome, Firefox, Safari, curl, and others on modern Node releases.\n\n## Installation\n\nThis requires Node >= 24.15.0, and >= 26.4.0 is strongly recommended as it's required to fully match browser fingerprints.\n\n```bash\nnpm install tls-impersonate\n```\n\nThis is a native module, but prebuilds are included for most platforms.\n\n## Usage\n\nPass a full ClientHello specification to `impersonate()`, then spread `...tlsOptions` into your connection options:\n\n```typescript\nimport * as tls from 'node:tls';\nimport { impersonate } from 'tls-impersonate';\n\nconst { tlsOptions, unsupported } = impersonate({\n    cipherSuites: [0x1301, 0x1302, 0x1303, 0xc02b, 0xc02f, /* ... */],\n    extensions: [\n        { type: 0x0000 }, // server_name\n        { type: 0x0017 }, // extended_master_secret\n        { type: 0x0010 }, // ALPN\n        // ...in wire order, including GREASE\n    ],\n    supportedGroups: [0x001d, 0x0017, 0x0018],\n    signatureAlgorithms: [0x0403, 0x0804, 0x0401],\n    alpnProtocols: ['h2', 'http/1.1']\n});\n\nconst socket = tls.connect({\n    host: 'testserver.host',\n    port: 443,\n    servername: 'testserver.host',\n    ...tlsOptions\n});\n```\n\nThe options configured in TLS options are those supported by Node's built-in [`tls` module](https://nodejs.org/api/tls.html) and can be passed to any function which accepts these - this includes the built-in `https` module and anything else which passes options through to it.\n\n`impersonate()` never throws for parts of the fingerprint it can't reproduce - it always produces the closest achievable ClientHello and reports any gaps in the `unsupported` array, so you can log or monitor them:\n\n```typescript\nfor (const gap of unsupported) {\n    console.warn(`Could not reproduce ${gap.kind} ${gap.id}: ${gap.reason}`);\n}\n```\n\nSome hellos can't be mirrored at all, and for those `impersonate()` throws a\n`CannotImpersonateError` (`code: 'ERR_CANNOT_IMPERSONATE'`). That happens if mirroring\nwith the given configuration would never successfully make a connection. For example,\nusing the default `secure` mode (TLS 1.2+) with a very old TLS 1.0 client hello would\ncreate a configuration that always fails.\n\n```typescript\nimport { impersonate, CannotImpersonateError } from 'tls-impersonate';\n\ntry {\n    const { tlsOptions } = impersonate(spec);\n    // ...connect with the mirrored fingerprint\n} catch (e) {\n    if (!(e instanceof CannotImpersonateError)) throw e;\n    // e.unsupported explains why. Connect with your own default fingerprint instead.\n}\n```\n\n### Impersonating a captured ClientHello\n\nIf you have a real ClientHello parsed with [read-tls-client-hello](https://github.com/httptoolkit/read-tls-client-hello), you can pass it straight in - no need to build a spec by hand:\n\n```typescript\nimport { impersonateFromClientHello } from 'tls-impersonate';\nimport { readTlsClientHello } from 'read-tls-client-hello';\n\nconst hello = await readTlsClientHello(capturedStream);\nconst { tlsOptions } = impersonateFromClientHello(hello);\n\nconst socket = tls.connect({\n    host: 'testserver.host',\n    port: 443,\n    servername: 'testserver.host',\n    ...tlsOptions\n});\n```\n\n### Security\n\nBy default, tls-impersonate will never reduce the security of TLS connections. It will not change the OpenSSL security level, and although it advertises some legacy features it will reject any connections that attempt to use them.\n\nThis works for almost all fingerprints, but not 100% of cases. If this is not sufficient (as reported by the `unsupported` result) you can pass `security: 'insecure'` to allow tls-impersonate to enable known-insecure configurations. This should be avoided unless absolutely necessary, and this isn't required to match the fingerprints of most clients (browsers, Android HTTP libraries, etc).\n\n### Feature detection\n\nImpersonation depends on internals that are only present on supported modern Node runtimes. Check availability once at startup and fall back to Node's default TLS if it's missing:\n\n```typescript\nimport { isSupported } from 'tls-impersonate';\n\nif (!isSupported()) {\n    // Wrong Node version, unsupported platform, or the native addon failed to load.\n}\n```","readmeFilename":"README.md"}