Skip to content

LyleMi/ja3proxy

Repository files navigation

JA3Proxy logo

JA3Proxy

JA3Proxy is an HTTP/SOCKS5 proxy that uses uTLS to create outbound TLS connections with configurable ClientHello fingerprints. It can be used to test how applications behave behind different browser-like TLS fingerprints, while keeping familiar proxy interfaces for clients.

Features

  • HTTP, HTTPS, and SOCKS5 proxy support on the same listen address.
  • Customizable TLS ClientHello fingerprints through uTLS presets.
  • Dynamic MITM certificates for HTTPS CONNECT traffic.
  • Automatic local CA generation when no certificate/key pair is provided.
  • Optional SOCKS5 upstream proxy for both HTTP and HTTPS traffic.
  • Docker and Docker Compose examples included.

How it works

For plain HTTP requests, JA3Proxy forwards the request directly. For HTTPS CONNECT requests, it establishes a TLS connection to the upstream server using the configured uTLS fingerprint, then serves a dynamically generated certificate to the client using the local CA. SOCKS5 connections are accepted on the same listen address: TLS streams use the same MITM/uTLS path, while non-TLS streams are forwarded as plain TCP.

Because HTTPS traffic is intercepted, clients must either trust the generated CA certificate or explicitly skip certificate verification for testing.

Quick start

Build from source

Requirements:

  • Go 1.24 or newer
  • make if you want to use the provided Makefile
git clone https://github.com/lylemi/ja3proxy.git
cd ja3proxy

go build -o ja3proxy ./cmd/ja3proxy
./ja3proxy -port 8080 -client 360Browser -version 7.5

The CLI source lives in cmd/ja3proxy.

Test the proxy:

curl -v -k --proxy http://127.0.0.1:8080 https://www.example.com
curl -v -k --proxy socks5h://127.0.0.1:8080 https://www.example.com

The first run creates credentials/cert.pem and credentials/key.pem if they do not already exist.

Docker

mkdir -p credentials

docker run --rm \
  -v ./credentials:/app/credentials \
  -p 8080:8080 \
  ghcr.io/lylemi/ja3proxy:latest \
  -cert /app/credentials/cert.pem \
  -key /app/credentials/key.pem \
  -client 360Browser \
  -version 7.5

Docker Compose

docker compose up -d

See compose.yaml for the full service definition.

Configuration

Usage of ja3proxy:
  -addr string
        proxy listen host
  -port string
        proxy listen port (default "8080")
  -cert string
        proxy CA cert (default "credentials/cert.pem")
  -key string
        proxy CA key (default "credentials/key.pem")
  -client string
        utls client (default "Golang")
  -version string
        utls client version (default "0")
  -fingerprint-config string
        JSON file to hot-reload utls client/version
  -upstream-tls-config string
        JSON file with upstream TLS profiles
  -upstream string
        upstream proxy, e.g. 127.0.0.1:1080, socks5 only
  -debug
        enable debug

Example with a SOCKS5 upstream proxy:

./ja3proxy \
  -port 8080 \
  -client Chrome \
  -version 106 \
  -upstream socks5://127.0.0.1:1080

The -upstream flag also accepts host:port, for example 127.0.0.1:1080. Only SOCKS5 upstream proxies are supported.

Upstream TLS profile config

Use -upstream-tls-config when different upstream hosts need different outbound TLS fingerprints. The flag loads a JSON file with a default upstream TLS profile and optional host-specific routes:

{
  "default": {
    "protocol": "utls",
    "client": "Chrome",
    "version": "120"
  },
  "routes": [
    {
      "host": "*.gm.example.com",
      "protocol": "utls",
      "client": "360Browser",
      "version": "7.5"
    },
    {
      "host": "api.example.com",
      "protocol": "utls",
      "client": "Firefox",
      "version": "105"
    }
  ]
}

Start the proxy with the profile file:

./ja3proxy -port 8080 -upstream-tls-config upstream-tls.json

JA3Proxy uses the default profile when no route matches. If default is omitted, unmatched hosts continue to use the global fingerprint from -client, -version, or -fingerprint-config.

Each profile currently supports protocol: "utls" with the same client and version values described in TLS fingerprints. tlcp is intentionally not implemented yet, so tlcp profiles are not supported in this version.

Route host values match the upstream destination host. Matching supports exact hosts such as api.example.com and leading wildcard patterns such as *.example.com.

This differs from -fingerprint-config: -fingerprint-config hot-reloads one global uTLS client/version pair for all upstream hosts, while -upstream-tls-config selects a profile by upstream host from the JSON file. The existing -client, -version, and -fingerprint-config flags remain backwards-compatible shortcuts for configuring one global uTLS fingerprint.

Hot-reload TLS fingerprints

Use -fingerprint-config to load the uTLS fingerprint from a JSON file and watch it for changes:

{
  "client": "Chrome",
  "version": "106"
}

Start the proxy with the file:

./ja3proxy -port 8080 -fingerprint-config fingerprint.json

When the file changes, JA3Proxy validates and reloads it. New HTTPS CONNECT connections use the latest fingerprint; existing TLS tunnels keep the fingerprint they were opened with. If a reload fails, the previous fingerprint stays active and the error is logged.

TLS fingerprints

JA3Proxy resolves global fingerprint settings and upstream TLS profiles to uTLS ClientHello presets. With the global shortcut flags, JA3Proxy passes the -client and -version values to uTLS. With -upstream-tls-config, each matched profile supplies the client and version values. Supported presets depend on the uTLS version used by this project. See the uTLS ClientHelloID definitions for the authoritative list.

Common presets:

Client Version
Golang 0
Firefox 55, 56, 63, 99, 105
Chrome 58, 62, 70, 96, 102, 106
iOS 12.1, 13, 14
Android 11
Edge 85, 106
Safari 16.0
360Browser 7.5
QQBrowser 11.1

Updating uTLS

The uTLS library is compiled into the JA3Proxy binary, so updating it requires a rebuild. Dependabot is configured to open weekly pull requests for Go module updates, including github.com/refraction-networking/utls. Those pull requests run the Go CI workflow before they are merged.

To update manually:

go get github.com/refraction-networking/utls@latest
go mod tidy
go test ./...

Certificates

JA3Proxy needs a CA certificate and private key to generate per-host certificates for HTTPS interception.

  • If both files exist, they are loaded from -cert and -key.
  • If neither file exists, JA3Proxy generates a new CA pair.
  • If only one file exists, startup fails to avoid using a mismatched pair.

By default, generated CA files are written to credentials/cert.pem and credentials/key.pem. If the configured paths include missing directories, JA3Proxy creates them before writing the files.

For browser or application testing, import the generated CA certificate into the client trust store. For one-off command-line checks, tools such as curl -k can skip verification.

Development

Run the test suite:

go test ./...

Build release binaries with the Makefile:

make

This creates Linux and Windows AMD64 binaries in the bin/ directory.

Security notice

JA3Proxy performs TLS interception and can expose decrypted traffic to the machine running the proxy. Use it only in environments where you have permission to inspect the traffic. Protect generated CA private keys carefully and remove them from client trust stores when they are no longer needed.

Contributing

Issues and pull requests are welcome. Please include a clear description, reproduction steps when reporting bugs, and tests for behavior changes when practical.

License

This project is licensed under the MIT License.

About

Customizing TLS (JA3) Fingerprints through HTTP Proxy

Topics

Resources

License

Stars

203 stars

Watchers

6 watching

Forks

Packages

 
 
 

Contributors

Languages