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 --save
or
yarn add node-libcurl-ja3
The 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:
impersonate
in place ofcurly
Curl.impersonate
in 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); // 8e19337e7524d2573be54efb2b0784c9
const { 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-source
With yarn:
npm_config_build_from_source=true yarn add node-libcurl-ja3
The 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-config
Users 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 -p
It should return their path, in case it returns nothing, you must install it by running:
xcode-select --install
Finally, install the remaining packages using homebrew:
brew install automake bash cmake libtool make meson ninja
Read 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.