An HTTP/HTTPS forward proxy that distributes client requests across HTTP, HTTPS, SOCKS4, and SOCKS5 upstream proxies. It supports Basic authentication, HTTP/1.1 and HTTP/2 CONNECT, and bounded failover.
.env, process environment, or strict YAMLDownload a binary and SHA256SUMS from GitHub Releases. On Linux, check the downloaded files in the same directory:
sha256sum --ignore-missing -c SHA256SUMS
Release assets include binaries for Linux, macOS, and Windows on amd64 and arm64. The env template is named env.example in the release assets; copy it to .env before editing. The assets contain examples but no working credentials or TLS keys.
Images are published to Docker Hub. To build the version from this checkout locally:
make docker-build
After preparing .env and certificate files, run the local image:
docker run -d --name go_proxy_mux -p 8380:8380 \
--user "$(id -u):$(id -g)" --read-only --cap-drop ALL \
--security-opt no-new-privileges \
--env-file .env \
-v "$PWD/certs:/app/certs:ro" \
"hightemp/go_proxy_mux:$(cat VERSION)"
Use Docker Compose for either .env or config.yaml configuration.
Go 1.26.7 or newer is required.
git clone https://github.com/hightemp/go_proxy_mux.git
cd go_proxy_mux
make build
make build-static creates go_proxy_mux_static with CGO disabled. Both targets build for the current system. GitHub Actions builds the cross-platform release binaries.
cmd/go_proxy_mux/ application entry point
internal/config/ YAML, .env, environment overrides, and validation
internal/balancer/ upstream selection and cooldown
internal/proxy/ authentication, forwarding, tunnels, and server lifecycle
internal/socks/ SOCKS4/SOCKS4a and SOCKS5 TCP dialing
Copy .env.example to .env, replace all placeholder credentials and upstream addresses, and provide a certificate and key if the listener is exposed over the network:
cp .env.example .env
chmod 600 .env
Set MUX_UPSTREAM_COUNT to the number of upstream entries. Number their fields consecutively from MUX_UPSTREAM_1_* to MUX_UPSTREAM_N_*. The example intentionally fails validation until its placeholders and TLS files are replaced.
Settings take precedence in this order: process environment → .env → YAML → built-in defaults. The -env flag selects an env file (.env by default); -env "" disables it. The -config flag selects a YAML file (config.yaml by default). If YAML is absent, an env-only configuration must provide the upstream list.
.env uses literal KEY=VALUE lines. Do not wrap values in shell quotes. Values may contain #, $, or =; multiline values are not supported. MUX_PROXY_TIMEOUT is an integer number of seconds. Other timeout settings use Go duration syntax such as 15s or 2m.
Every application setting is represented in .env.example. MUX_PUBLISH_HOST, MUX_PUBLISH_PORT, PROXY_RUN_UID, and PROXY_RUN_GID affect only Docker Compose.
| Variable | Purpose |
|---|---|
MUX_SERVER_HOST, MUX_SERVER_PORT | Listener address and port |
MUX_SERVER_TLS_CERT_FILE, MUX_SERVER_TLS_KEY_FILE | TLS certificate and private key paths; both empty selects HTTP |
MUX_SERVER_ALLOW_INSECURE_PUBLIC_HTTP | Explicitly allow non-loopback HTTP |
MUX_SERVER_ALLOW_UNAUTHENTICATED_PUBLIC_PROXY | Explicitly allow a non-loopback listener without client auth |
MUX_SERVER_MAX_CONNECTIONS | Maximum active client TCP connections |
MUX_SERVER_MAX_CONNECTIONS_PER_IP | Maximum active client connections per source IP |
MUX_SERVER_MAX_HEADER_BYTES | Incoming HTTP request-header limit |
MUX_SERVER_HTTP2_MAX_CONCURRENT_STREAMS | HTTP/2 stream limit; 0 derives it from tunnel limits |
MUX_SERVER_HTTP2_SEND_PING_TIMEOUT, MUX_SERVER_HTTP2_PING_TIMEOUT, MUX_SERVER_HTTP2_WRITE_BYTE_TIMEOUT | HTTP/2 connection health and stalled-write timeouts |
MUX_SERVER_READ_HEADER_TIMEOUT, MUX_SERVER_IDLE_TIMEOUT, MUX_SERVER_SHUTDOWN_TIMEOUT | Listener and shutdown timeouts |
MUX_AUTH_ENABLED, MUX_AUTH_USERNAME, MUX_AUTH_PASSWORD | Client Basic authentication |
MUX_AUTH_MAX_FAILED_ATTEMPTS, MUX_AUTH_FAILURE_WINDOW, MUX_AUTH_BLOCK_DURATION, MUX_AUTH_MAX_TRACKED_IPS | Failed login throttling per source IP |
MUX_PROXY_ALGORITHM | roundrobin or random |
MUX_PROXY_TIMEOUT | HTTP setup through response headers and CONNECT setup timeout, in seconds |
MUX_PROXY_NETWORK | Outbound auto, tcp4, or tcp6 dialing |
MUX_PROXY_DIAL_TIMEOUT, MUX_PROXY_DIAL_KEEP_ALIVE | TCP dial and keep-alive durations |
MUX_PROXY_TLS_HANDSHAKE_TIMEOUT, MUX_PROXY_RESPONSE_HEADER_TIMEOUT | Outbound TLS and response-header deadlines |
MUX_PROXY_RESPONSE_BODY_IDLE_TIMEOUT | Maximum pause between bytes read from an HTTP response body |
MUX_PROXY_IDLE_CONN_TIMEOUT, MUX_PROXY_EXPECT_CONTINUE_TIMEOUT | HTTP transport idle and Expect: 100-continue timeouts |
MUX_PROXY_MAX_IDLE_CONNS, MUX_PROXY_MAX_IDLE_CONNS_PER_HOST, MUX_PROXY_MAX_CONNS_PER_HOST | HTTP transport connection-pool limits |
MUX_PROXY_MAX_TUNNELS, MUX_PROXY_TUNNEL_IDLE_TIMEOUT | Active tunnel limit and idle timeout |
MUX_PROXY_MAX_TUNNELS_PER_IP | Maximum active tunnels per source IP |
MUX_PROXY_FAILOVER_COOLDOWN | Time to exclude a failed upstream |
MUX_UPSTREAM_COUNT | Number of upstream entries |
MUX_UPSTREAM_N_URL | Upstream URL: http://, https://, socks4://, or socks5:// |
MUX_UPSTREAM_N_AUTH_ENABLED, MUX_UPSTREAM_N_AUTH_USERNAME, MUX_UPSTREAM_N_AUTH_PASSWORD | Credentials for upstream N |
MUX_UPSTREAM_N_TLS_CA_FILE | Optional private CA file for an HTTPS upstream |
MUX_PUBLISH_HOST, MUX_PUBLISH_PORT | Host-side Compose binding only |
PROXY_RUN_UID, PROXY_RUN_GID | Container process identity in Compose only |
MUX_PROXY_TIMEOUT limits forwarded HTTP requests until the response headers arrive, and limits CONNECT setup. It does not cap the total duration of an HTTP response body. MUX_PROXY_RESPONSE_BODY_IDLE_TIMEOUT defaults to 2 minutes and closes a response only when no body bytes arrive during that interval. Large downloads can run for hours while data keeps arriving. The separate dial, TLS handshake, and response-header timeouts apply within the setup deadline. MUX_PROXY_NETWORK selects automatic, IPv4-only, or IPv6-only TCP dialing to an upstream proxy; SOCKS upstreams still resolve destination hostnames themselves.
The transport settings control idle connection reuse and limits per destination host. MUX_PROXY_MAX_CONNS_PER_HOST=0 leaves the total per-host limit unlimited. The HTTP/2 SETTINGS stream limit defaults to the lower of the global and per-IP tunnel limits; ping and stalled-write timeouts use Go's net/http.HTTP2Config.
YAML is also supported; see config.example.yaml. A minimal TLS example is:
server:
host: "0.0.0.0"
port: 8380
tls:
cert_file: "./certs/fullchain.pem"
key_file: "./certs/privkey.pem"
auth:
enabled: true
username: "<set-username>"
password: "<set-strong-password>"
max_failed_attempts: 10
failure_window: 1m
block_duration: 5m
max_tracked_ips: 4096
proxy:
algorithm: roundrobin
timeout: 30
response_body_idle_timeout: 2m
upstreams:
- url: "socks5://socks.example.net:1080"
auth:
enabled: true
username: "<upstream-username>"
password: "<upstream-password>"
Replace the placeholders before use. Unknown YAML keys and invalid settings stop startup. To run only from YAML when .env is present in the project directory:
./go_proxy_mux -config config.yaml -env ""
For local HTTP, bind 127.0.0.1 and leave both TLS paths empty. A listener on another address requires TLS unless allow_insecure_public_http is explicitly enabled. Public access without client authentication separately requires allow_unauthenticated_public_proxy. The shipped examples use TLS and Basic authentication.
The certificate must match the hostname or IP used by clients. Certificate and key files are validated at startup. Renew them externally and restart the proxy to load replacements. The Docker image contains neither configuration secrets nor TLS keys.
By default, the tenth failed Basic authentication attempt from one source IP within a one-minute window blocks that IP for 5 minutes. The window begins with its first failure. Earlier failures receive 407; blocked requests receive 429 with Retry-After, including requests with correct credentials until the block expires. A successful login before the block clears the failure count. Clients sharing one public IP also share its limit. max_tracked_ips bounds in-memory failure records (4096 by default); when full, the oldest record is evicted. Counters reset when the process restarts.
Each upstream is selected by its URL scheme. HTTP and HTTPS upstreams can use Basic authentication. An HTTPS upstream validates its certificate against system roots; set tls_ca_file only for a private CA.
SOCKS4 supports an optional USERID in auth.username and no password. Domain destinations use SOCKS4a; IPv6 destinations require SOCKS5. SOCKS5 supports either no authentication or a username/password. Destination hostnames are sent to the SOCKS upstream for resolution. SOCKS5 username/password is sent to that upstream without encryption. Keep upstream credentials in auth, not in the URL.
roundrobin and random choose among available upstreams. A network or CONNECT setup failure excludes an upstream for failover_cooldown (30 seconds by default). Before responding to the client, a bodyless GET/HEAD or CONNECT may try one alternate upstream. POST and other requests with bodies are never replayed. An upstream 407 becomes a client-facing 502, so clients are not asked for the upstream's credentials. There is no automatic direct connection if all upstreams fail.
max_connections and max_tunnels bound active client TCP connections and CONNECT tunnels globally; their *_per_ip counterparts also limit each source IP and cannot exceed the global limits. max_header_bytes bounds incoming request headers. The HTTP/2 stream limit defaults to the lower tunnel limit and cannot exceed either tunnel limit; ping and stalled-write timeouts can be tuned separately. A tunnel closes after tunnel_idle_timeout without traffic. SIGINT and SIGTERM stop new requests and close tracked tunnels within shutdown_timeout.
Two Compose files provide separate configuration paths:
| Configuration | Command | Notes |
|---|---|---|
.env | docker compose up --build | docker-compose.yml passes .env as raw values; Docker Compose 2.30+ required. MUX_PUBLISH_HOST:MUX_PUBLISH_PORT maps to MUX_SERVER_PORT. |
config.yaml | docker compose -f docker-compose.config.yml up --build | docker-compose.config.yml mounts YAML read-only and disables .env loading in the application. It publishes port 8380; update the mapping if server.port differs. |
Both variants mount certs/ read-only and run with a read-only root filesystem, no Linux capabilities, and no privilege escalation. The image defaults to UID/GID 1000; Compose uses PROXY_RUN_UID and PROXY_RUN_GID (also 1000 by default). Use a non-root UID that can read your mode-0600 configuration and key files, or grant the chosen group read access. Set server.host (or MUX_SERVER_HOST) to 0.0.0.0 inside the container. The certificate and key paths in the examples resolve under /app/certs/. Base images are pinned by digest and checked for updates weekly by Dependabot.
Start with .env:
./go_proxy_mux -env .env
A local HTTP listener with client authentication disabled can proxy an HTTPS website using CONNECT:
curl --proxy http://127.0.0.1:8380 https://example.com
For a TLS listener, configure clients with an HTTPS proxy URL, the certificate's hostname, and client credentials. Both incoming modes can carry HTTPS site traffic; TLS on the listener protects the client-to-proxy connection.
| Command | Description |
|---|---|
make build | Build a normal binary for the current system |
make build-static | Build go_proxy_mux_static with CGO disabled |
make run | Run from the project directory; .env is loaded when present |
make ci | Check formatting, vet, lint, and run race-enabled tests |
make load-test | Run concurrent local HTTP and CONNECT benchmarks |
make docker-build | Build hightemp/go_proxy_mux:<VERSION> locally |
make docker-push | Push that local image to Docker Hub |
make clean | Remove local normal and static binaries |
make release | Check, build, commit, tag VERSION, and atomically push main and the tag |
make ci
CI validates both Compose variants and builds the Docker image. Integration tests cover HTTP forwarding, HTTP/1.1 and HTTP/2 CONNECT, upstream TLS, SOCKS4/SOCKS5, authentication, failover, cancellation, and shutdown.
make load-test measures authenticated small and 64 KiB HTTP responses plus HTTP/1.1, HTTP/2, and SOCKS5 CONNECT echo tunnels through local upstreams at 32 workers (-cpu=4, parallelism 8). The regular test suite checks that slow client tunnels release their slots. The benchmarks report throughput and allocations for this machine; they do not measure remote upstream latency or Internet bandwidth.
DOCKERHUB_USERNAME and DOCKERHUB_TOKEN for hightemp/go_proxy_mux.main branch, run make release.The Make target runs CI and builds locally, commits project changes when present, creates an annotated v<version> tag on the resulting commit, then pushes the branch and tag atomically. A clean checkout tags the current commit. It refuses to overwrite an existing tag. A failed push can be retried with the same command if the local commit and tag are intact.
The tag triggers GitHub Actions release after CI passes. The workflow publishes six binaries, examples, and SHA256SUMS to GitHub Releases; it also publishes linux/amd64 and linux/arm64 images to Docker Hub. Stable releases update latest. The workflow checks the published image platforms and release asset checksums.
Content type
Image
Digest
sha256:1c7207bb2…
Size
9.5 MB
Last updated
13 days ago
docker pull hightemp/go_proxy_mux