This is a fork of node-libcurl using patches from lexiforest/curl-impersonate
to impersonate the four major browsers: Chrome, Edge, Safari and Firefox. node-libcurl-ja3 is able to
perform TLS and HTTP handshakes that are identical to that of a real browser.
Only the following platforms are supported:
- Linux 64-bit (glibc based)
- macOS Apple Silicon (M1+)
Prebuilt binaries are provided for Node.js 20 and 22. Any other version has not been tested and will need an
environment capable of building the native module. Refer to Important Notes on Prebuilt Binaries / Direct Installation
for a list of required system packages.
Although the library is named node-libcurl-ja3, it also supports http2 and ja4 impersonation (e.g. Akamai).
- Quick Start
- API
- Special Notes
- Common Issues
- Benchmarks
- Detailed Installation
- Contributing
- Acknowledgments
- This library cannot be used in a browser, it depends on native code.
- There is no worker threads support at the moment. See #169
npm i node-libcurl-ja3 --saveor
yarn add node-libcurl-ja3The following browser fingerprints are pre-configured:
- Chrome 134
- Edge 134
- Firefox 136.0
- Safari 18.3
To learn how to configure custom impersonation options, refer to the folder lib/impersonate/browser.
For brevity, this section covers a single example for creating impersonate instances using the curly API and Curl class. To use impersonation with the examples following this section, adapt them to use either:
impersonatein place ofcurlyCurl.impersonatein place ofnew Curl
const { Browser, impersonate } = require('node-libcurl-ja3');
const curly = impersonate(Browser.Chrome);
const { data } = await curly.get('https://tls.browserleaks.com/json');
console.log(data.ja3n_hash); // 8e19337e7524d2573be54efb2b0784c9const { Browser, Curl } = require('node-libcurl-ja3');
const curl = Curl.impersonate(Browser.Chrome);
curl.setOpt('URL', 'tls.browserleaks.com/json');
curl.setOpt('FOLLOWLOCATION', true);
curl.on('end', function (statusCode, data, headers) {
console.info(statusCode);
console.info('---');
console.info(data.length);
console.info('---');
console.info(this.getInfo('TOTAL_TIME'));
this.close();
});
curl.on('error', curl.close.bind(curl));
curl.perform();this API is experimental and is subject to changes without a major version bump
const { curly } = require('node-libcurl-ja3');
const { statusCode, data, headers } = await curly.get('http://www.google.com')Any option can be passed using their FULLNAME or a lowerPascalCase format:
const querystring = require('querystring');
const { curly } = require('node-libcurl-ja3');
const { statusCode, data, headers } = await curly.post('http://httpbin.com/post', {
postFields: querystring.stringify({
field: 'value',
}),
// can use `postFields` or `POSTFIELDS`
})JSON POST example:
const { curly } = require('node-libcurl-ja3')
const { data } = await curly.post('http://httpbin.com/post', {
postFields: JSON.stringify({ field: 'value' }),
httpHeader: [
'Content-Type: application/json',
'Accept: application/json'
],
})
console.log(data)const { Curl } = require('node-libcurl-ja3');
const curl = new Curl();
curl.setOpt('URL', 'www.google.com');
curl.setOpt('FOLLOWLOCATION', true);
curl.on('end', function (statusCode, data, headers) {
console.info(statusCode);
console.info('---');
console.info(data.length);
console.info('---');
console.info(this.getInfo( 'TOTAL_TIME'));
this.close();
});
curl.on('error', curl.close.bind(curl));
curl.perform();Pass an array of strings specifying headers
curl.setOpt(Curl.option.HTTPHEADER,
['Content-Type: application/x-amz-json-1.1'])const querystring = require('querystring');
const { Curl } = require('node-libcurl-ja3');
const curl = new Curl();
const close = curl.close.bind(curl);
curl.setOpt(Curl.option.URL, '127.0.0.1/upload');
curl.setOpt(Curl.option.POST, true)
curl.setOpt(Curl.option.POSTFIELDS, querystring.stringify({
field: 'value',
}));
curl.on('end', close);
curl.on('error', close);const { Curl } = require('node-libcurl-ja3');
const curl = new Curl();
const close = curl.close.bind(curl);
curl.setOpt(Curl.option.URL, '127.0.0.1/upload.php');
curl.setOpt(Curl.option.HTTPPOST, [
{ name: 'input-name', file: '/file/path', type: 'text/html' },
{ name: 'input-name2', contents: 'field-contents' }
]);
curl.on('end', close);
curl.on('error', close);When requesting binary data make sure to do one of these:
- Pass your own
WRITEFUNCTION(https://curl.haxx.se/libcurl/c/CURLOPT_WRITEFUNCTION.html):
curl.setOpt('WRITEFUNCTION', (buffer, size, nmemb) => {
// something
})- Enable one of the following flags:
curl.enable(CurlFeature.NoDataParsing)
// or
curl.enable(CurlFeature.Raw)The reasoning behind this is that by default, the Curl instance will try to decode the received data and headers to utf8 strings, as can be seen here: https://github.com/JCMais/node-libcurl/blob/b55b13529c9d11fdcdd7959137d8030b39427800/lib/Curl.ts#L391
For more examples check the examples folder.
This library provides Typescript type definitions.
Almost all CURL options are supported, if you pass one that is not, an error will be thrown.
For more usage examples check the examples folder.
The buffer passed as first parameter to the callback set with the READFUNCTION option is initialized with the size libcurl is using in their upload buffer (which can be set with UPLOAD_BUFFERSIZE), this is initialized using node::Buffer::Data(buf); which is basically the same than Buffer#allocUnsafe and therefore, it has all the implications as to its correct usage: https://nodejs.org/pt-br/docs/guides/buffer-constructor-deprecation/#regarding-buffer-allocunsafe
So, be careful, make sure to return exactly the amount of data you have written to the buffer on this callback. Only that specific amount is going to be copied and handed over to libcurl.
See COMMON_ISSUES.md
See ./benchmark
The latest version of this package has prebuilt binaries (thanks to node-pre-gyp) available for:
- Node.js: Latest two versions on active LTS (see https://github.com/nodejs/Release)
And on the following platforms:
- Linux 64-bit (glibc based)
- macOS Apple Silicon (M1+)
Installing with yarn add node-libcurl-ja3 or npm install node-libcurl-ja3 should download a prebuilt binary and no compilation will be needed.
The prebuilt binary is statically built with the following library versions, features and protocols:
Versions: libcurl/8.7.0-DEV BoringSSL zlib/1.3 brotli/1.1.0 zstd/1.5.6 nghttp2/1.63.0
Protocols: dict file ftp ftps gopher gophers http https imap imaps ipfs ipns mqtt pop3 pop3s rtsp smb smbs smtp smtps telnet tftp ws wss
Features: alt-svc AsynchDNS brotli HSTS HTTP2 HTTPS-proxy IPv6 Largefile libz NTLM SSL threadsafe UnixSockets zstd
If there is no prebuilt binary available that matches your system, or if the installation fails, then you will need an environment capable of compiling Node.js addons, which means:
- python 3.x installed
- updated C++ compiler able to compile C++17.
If you don't want to use the prebuilt binary even if it works on your system, you can pass a flag when installing:
With npm:
npm install node-libcurl-ja3 --build-from-sourceWith yarn:
npm_config_build_from_source=true yarn add node-libcurl-ja3The built binaries are statically linked with BoringSSL, brotli, nghttp2, zlib and zstd.
The built binaries do not have support for GSASL, GSS-API, HTTP3, IDN, LDAP, LDAPS, PSL, RTMP, SPNEGO, SSH, SSPI or TLS-SRP.
If you are on a debian based system, install the required dependencies by running:
sudo apt install -qqy autoconf automake build-essential cmake curl libtool ninja-build pkg-configUsers for other distributions will need to find the equivalent packages and install via your package manager.
On macOS you must have:
- macOS >= 11.6 (Big Sur)
- Xcode Command Line Tools
- Homebrew
- Bash >= 5.0 (unconfirmed)
You can check if you have Xcode Command Line Tools be running:
xcode-select -pIt should return their path, in case it returns nothing, you must install it by running:
xcode-select --installFinally, install the remaining packages using homebrew:
brew install automake bash cmake libtool make meson ninjaRead CONTRIBUTING.md
- JCMais/node-libcurl — provides the node libcurl bindings upon which this fork is created from.
- lexiforest/curl-impersonate — provides patches to add impersonate behaviour to curl.
- galihrivanto/node-libcurli — a similar fork based upon an older version of node-libcurl and libcurl-impersonate.
- Ossianaa/node-libcurl — a similar library which provided inspiration for setting fingerprints from JA3.