On a Linux server, you’ll commonly run into problems like curl failing to connect, git clone being painfully slow, dnf/apt/pip downloads crawling along, a program only supporting an HTTP proxy when all you have is SOCKS5, Docker failing to pull an image, or even wanting all traffic to automatically go through a proxy. All of these get lumped together under “setting up a proxy,” but they actually involve completely different layers of the network stack.
Core insight: Linux has no single unified switch that makes every program automatically go through a proxy. What you’re actually configuring might be a particular program’s command-line flag, a shell environment variable, socket interception via
LD_PRELOAD, or routing-layer TUN/TPROXY. Understanding this is the key to actually mastering proxy configuration.
1. Basic Concepts: Proxy Types and Environment Variables
1.1 Common Proxy Protocols
HTTP Proxy
- Typically on port 8080 or 3128.
- Can proxy both HTTP and HTTPS traffic. For HTTPS, the client first sends
CONNECT example.com:443to establish a TCP tunnel, and TLS is then negotiated directly between the client and the target — the proxy just forwards the encrypted byte stream. - Requires explicit client configuration, and doesn’t support UDP.
HTTPS Proxy (HTTP over TLS)
- Refers to the communication between the client and the proxy server itself being encrypted with TLS — i.e., the proxy address is
https://proxy.example.com:8080. - The underlying protocol is still an HTTP proxy (the
CONNECTmethod), but connecting to the proxy itself goes through a TLS handshake, preventing a man-in-the-middle from snooping. - Many CLI tools (like curl) support pointing the
https_proxyvariable at anhttps://...address.
SOCKS5 Proxy
- Typically on port 1080.
- More general-purpose: supports both TCP and UDP, doesn’t care about the application-layer protocol, and works for any TCP/UDP traffic (like SSH, databases, games).
- A key DNS distinction:
socks5://: DNS resolution happens locally.socks5h://: DNS resolution is handed off to the proxy server (thehstands for hostname). This is recommended to avoid local DNS leaks or poisoning.
Shadowsocks
- A lightweight, secure proxy protocol built on top of SOCKS5, widely used to circumvent firewalls.
- The Linux client
ss-localsets up a standard SOCKS5 proxy locally (defaulting to 127.0.0.1:1080):ss-local -s server.example.com -p 8388 -l 1080 -k your_password -m aes-256-gcm - After that, any program supporting SOCKS5 can use
127.0.0.1:1080. - Related tools also include
ss-redir(transparent proxying) andss-tunnel(port forwarding).
1.2 Environment Variables in Detail (Case Sensitivity and no_proxy)
A large number of CLI tools on Linux (curl, Git, pip, apt, wget, etc.) follow these environment variables, but different programs support them, and treat their case-sensitivity, differently.
| Variable (lowercase) | Variable (uppercase) | Purpose |
|---|---|---|
http_proxy | HTTP_PROXY | Proxy used when accessing an HTTP URL |
https_proxy | HTTPS_PROXY | Proxy used when accessing an HTTPS URL (the value can be either http:// or https://) |
all_proxy | ALL_PROXY | Proxy used for any protocol (usually set to a SOCKS5 proxy) |
no_proxy | NO_PROXY | A comma-separated list of addresses that should bypass the proxy |
The case-sensitivity trap:
- Prefer lowercase: for security reasons,
curlignores the uppercaseHTTP_PROXY(to prevent a child process from accidentally inheriting it), so it’s recommended to at minimum ensure the lowercase variables exist. - To be compatible with more programs (some Java tools, older software, etc.), you can export both the lowercase and uppercase versions at once:
export http_proxy="http://127.0.0.1:8080" export HTTP_PROXY="$http_proxy" export https_proxy="http://127.0.0.1:8080" export HTTPS_PROXY="$https_proxy" export all_proxy="socks5h://127.0.0.1:1080" export ALL_PROXY="$all_proxy"
On https_proxy=http://...: this isn’t a typo. https_proxy means “the proxy used when accessing an HTTPS URL,” while the http://127.0.0.1:8080 that follows means the HTTP Proxy protocol is used when talking to the proxy itself (rather than SOCKS) — the two are entirely consistent with each other.
no_proxy matching rules:
- Example:
no_proxy="localhost,127.0.0.1,.example.com,192.168.0.0/16" - Different programs may not agree on the matching rules (whether wildcards, CIDR notation, or domain suffixes are supported). For instance, whether
.example.commatchesfoo.example.comneeds to be checked against the specific program’s documentation — don’t assume.
2. Setting Up a Proxy Server
Before you can use a proxy, you first need to “have” a proxy server. Below are three common ways to set one up, covering everything from the bare minimum to production-grade needs.
2.1 The Bare-Minimum Approach: SSH Dynamic Port Forwarding (SOCKS5)
If you already have a remote server you can SSH into, you don’t need to install any additional software at all — you can set up a SOCKS5 proxy using SSH’s built-in functionality directly.
ssh -N -D 127.0.0.1:1080 [email protected]
-D: dynamic port forwarding, sets up a SOCKS5 proxy.-N: don’t execute a remote command, only set up the tunnel.
Keeping it stable (adding keepalives):
ssh -N -D 127.0.0.1:1080 \
-o ServerAliveInterval=30 \
-o ServerAliveCountMax=3 \
-o ExitOnForwardFailure=yes \
[email protected]
This can also be written into ~/.ssh/config:
Host my-proxy
HostName server.example.com
User user
DynamicForward 127.0.0.1:1080
ServerAliveInterval 30
ServerAliveCountMax 3
ExitOnForwardFailure yes
After that, just run ssh -N my-proxy.
2.2 A Standard HTTP Proxy: Squid
Squid is the classic HTTP proxy server on Linux, well suited to providing a stable HTTP/HTTPS proxy for a LAN or a local machine.
Installation:
sudo apt install squid
sudo dnf install squid
Basic configuration (/etc/squid/squid.conf):
- Listening port (default 3128):
http_port 3128 - Allow local access (if only used locally):
acl localnet src 127.0.0.0/8 http_access allow localnet http_access deny all - To allow other machines on the LAN, add
acl localnet src 192.168.1.0/24.
Start it and enable it to launch on boot:
sudo systemctl start squid
sudo systemctl enable squid
Now you have an HTTP proxy at http://127.0.0.1:3128.
2.3 The Swiss Army Knife: Quickly Setting Up a Multi-Protocol Proxy with GOST
GOST is a lightweight proxy-forwarding tool — a single command can set up HTTP, SOCKS5, or convert between arbitrary protocols.
- Set up an HTTP proxy:
gost -L http://127.0.0.1:8080 - Set up a SOCKS5 proxy:
gost -L socks5://127.0.0.1:1080 - Enable both protocols at once:
gost -L http://127.0.0.1:8080 -L socks5://127.0.0.1:1080
Security note: unless you actually need to serve external traffic, always bind to
127.0.0.1rather than0.0.0.0, to avoid accidentally exposing the proxy to the public internet.
3. Client Configuration: Common Tools and Real-World Usage
Once you have a proxy server, the next step is getting various client tools to actually use it.
3.1 One-Off Use (Without Polluting the Shell)
Good for a single command, without changing the current shell’s environment:
https_proxy=http://127.0.0.1:8080 curl https://example.com
ALL_PROXY=socks5h://127.0.0.1:1080 git clone ...
3.2 Persistent Toggling (Shell Functions)
Add these to ~/.bashrc or ~/.zshrc:
proxy_on() {
export http_proxy="http://127.0.0.1:8080"
export https_proxy="http://127.0.0.1:8080"
export all_proxy="socks5h://127.0.0.1:1080"
export HTTP_PROXY="$http_proxy" HTTPS_PROXY="$https_proxy" ALL_PROXY="$all_proxy"
export no_proxy="localhost,127.0.0.1"
export NO_PROXY="$no_proxy"
echo "Proxy enabled"
}
proxy_off() {
unset http_proxy https_proxy all_proxy HTTP_PROXY HTTPS_PROXY ALL_PROXY no_proxy NO_PROXY
echo "Proxy disabled"
}
3.3 A Quick Reference for Configuring Each CLI Tool
| Tool | How to Configure |
|---|---|
| curl | -x http://... or -x socks5h://... |
| Git | Inherits environment variables; or git config --global http.proxy http://127.0.0.1:8080 |
| pip | pip install --proxy http://127.0.0.1:8080 pkg, or just use environment variables |
| npm | npm config set proxy http://127.0.0.1:8080 npm config set https-proxy http://127.0.0.1:8080 |
| DNF | Edit /etc/dnf/dnf.conf and add proxy=http://127.0.0.1:8080;or sudo https_proxy=... dnf update (note that sudo may clear the environment — use sudo -E to preserve it) |
| APT | Create /etc/apt/apt.conf.d/80proxy:Acquire::http::Proxy "http://127.0.0.1:8080";Acquire::https::Proxy "http://127.0.0.1:8080"; |
3.4 Getting Claude Code / Codex Through a Proxy: SSH Dynamic Forwarding + GOST + Environment Variables
If the local machine (or some server that can’t reach Anthropic / OpenAI directly) only has one server it can SSH into that can actually reach the outside world normally, you can wire up claude or codex to go out through that server’s network egress in three steps.
Step 1: SSH dynamic port forwarding, to get a local SOCKS5 proxy (same as section 2.1):
ssh -N -D 127.0.0.1:1080 \
-o ServerAliveInterval=30 \
-o ServerAliveCountMax=3 \
-o ExitOnForwardFailure=yes \
[email protected]
If you don’t have such a server on hand yet, you can first set one up by following Installing OCserv on Linux: Setting Up an OpenConnect VPN Server, then run the command above against it. It’s recommended to keep this running long-term in a dedicated terminal or tmux window.
Step 2: Use GOST to turn the SOCKS5 proxy into an HTTP proxy. Claude Code and Codex read HTTP_PROXY / HTTPS_PROXY, not SOCKS5, so you need one more layer of conversion (the same conversion approach as section 4.2):
gost -L http://127.0.0.1:8080 -F socks5://127.0.0.1:1080
Again, it’s recommended to keep this running persistently in its own terminal. At this point, http://127.0.0.1:8080 is an HTTP proxy that forwards to the remote egress.
Step 3: Export the environment variables and launch the CLI:
export http_proxy="http://127.0.0.1:8080"
export https_proxy="http://127.0.0.1:8080"
export HTTP_PROXY="$http_proxy"
export HTTPS_PROXY="$https_proxy"
claude
# or
codex
If you just want to use it once without changing the current shell, you can specify it directly in front of the command instead:
HTTPS_PROXY=http://127.0.0.1:8080 HTTP_PROXY=http://127.0.0.1:8080 claude
The three processes
ssh -D,gost, andclaude/codexall need to stay alive at the same time — if either of the first two exits, the whole proxy chain breaks along with it.
If you want to forward API requests to a custom backend rather than just forwarding the protocol, you can also have GOST reverse-proxy directly to the target address:
gost -L http://127.0.0.1:8080 -F http://target-api.example.com
3.5 systemd Services: Environment Variables Aren’t Inherited
Variables exported in a shell don’t affect a daemon launched by systemd. The correct approach is to create a drop-in directory:
sudo mkdir -p /etc/systemd/system/example.service.d
Write into proxy.conf:
[Service]
Environment="HTTP_PROXY=http://127.0.0.1:8080"
Environment="HTTPS_PROXY=http://127.0.0.1:8080"
Environment="NO_PROXY=localhost,127.0.0.1"
Then run sudo systemctl daemon-reload && sudo systemctl restart example.
3.6 Docker’s Three Layers of Proxy Confusion
“Setting up a proxy for Docker” refers to at least three different scenarios:
- dockerd pulling images: configure
/etc/docker/daemon.json:
or set the environment variables via a systemd drop-in (same as 3.5).{ "proxies": { "http-proxy": "http://127.0.0.1:8080", "https-proxy": "http://127.0.0.1:8080", "no-proxy": "localhost,127.0.0.1" } } - An application inside a container: pass the variable via
-e:docker run --rm -e http_proxy=http://proxy:8080 ubuntu env - The
docker buildbuild process: needs to be passed in via--build-arg.
The key gotcha:
127.0.0.1inside a container points to the container itself, not the host. If the proxy is listening on the host’s127.0.0.1, the container needs to use the host’s IP within the container network instead (e.g.,172.17.0.1) orhost.docker.internal(Mac/Windows).
4. Programs Without Proxy Support? ProxyChains and GOST Conversion
4.1 ProxyChains: Hijacking libc’s Networking Functions
For a program that has no proxy support at all and doesn’t read environment variables either, you can use ProxyChains to force it through a proxy. It intercepts calls like connect() via LD_PRELOAD.
Configuration (/etc/proxychains4.conf):
strict_chain
proxy_dns # also route DNS through the proxy, to avoid leaks
[ProxyList]
socks5 127.0.0.1 1080
Usage:
proxychains4 curl https://example.com
Limitations:
- Only handles TCP (UDP/ICMP, such as
ping, doesn’t work). - May fail for statically linked binaries, or for programs written in Go/Rust/Java that don’t go through the standard libc networking path.
- Not suitable for daemons or container scenarios.
It’s really just a “compatibility patch,” not a transparent proxy.
4.2 GOST Protocol Conversion: SOCKS5 ↔ HTTP
If all you have is SOCKS5, but some program only supports an HTTP proxy, you can convert on the spot with GOST:
gost -L http://127.0.0.1:8080 -F socks5://127.0.0.1:1080
Now 127.0.0.1:8080 is itself an HTTP proxy, with every request forwarded to the SOCKS5 proxy through GOST. The reverse (converting HTTP to SOCKS5) also works.
5. Transparent Proxying: From the Application Layer to the Network Layer (TUN/TPROXY)
Once the requirement escalates to “every program should go through the proxy without being aware of it,” you need to take over traffic at the network layer instead. Common approaches:
- REDIRECT: iptables/nftables redirects traffic to a local proxy port.
- TPROXY: more flexible, preserves the original destination address, needs to be paired with policy routing.
- TUN: creates a virtual network interface; the application’s IP packets are routed to that interface, and handled by the proxy program.
5.1 A Recommended Tool: sing-box
sing-box is a general-purpose proxy platform from SagerNet, written in Go. It inherits Clash Premium’s TUN inbound functionality, making it one of the most modern and sensible ways to implement a transparent proxy.
Key features:
- TUN mode: captures IP packets at the network layer (L3), converts them into L4 connections (TCP/UDP), and then hands them off to the routing system for processing. Unlike TPROXY, which relies on iptables, TUN only modifies the routing table, making it less likely to conflict with something like Docker’s networking.
auto_redirect: recommended to enable on Linux — it provides better routing support and automatically handles nftables rules.- Modular configuration: a clean structure that supports flexible DNS and routing rules.
A minimal TUN configuration example (/etc/sing-box/config.json):
{
"inbounds": [
{
"type": "tun",
"tag": "tun-in",
"interface_name": "tun0",
"address": ["172.19.0.1/30"],
"auto_route": true,
"auto_redirect": true
}
],
"outbounds": [
{
"type": "direct",
"tag": "direct"
}
]
}
Start it:
sudo systemctl start sing-box
After that, all system traffic is routed to the tun0 virtual network interface, and handled by sing-box according to its rules (which can forward traffic to a SOCKS5/HTTP outbound).
5.2 When Should You Use a Transparent Proxy?
- Only reach for TUN/TPROXY when you don’t want to configure programs one by one, and you’re willing to accept the extra complexity involved (DNS, routing loops, Docker networking, IPv6, MTU, and other things you’ll need to handle).
- Otherwise, environment variables or ProxyChains are far more lightweight.
6. Debugging and a Quick-Reference Cheat Sheet
6.1 Standard Troubleshooting Flow
- Confirm the proxy is actually listening:
ss -lntp | grep -E '1080|8080'— pay attention to whether it’s127.0.0.1or0.0.0.0. - Test the proxy directly:
curl -v -x socks5h://127.0.0.1:1080 https://example.com, and observe theCONNECTor TLS handshake in the output. - Check the environment variables:
env | grep -i proxy, making sure both cases exist with the correct values. - Diagnose DNS:
getent hosts example.com, or watch withtcpdump -ni any port 53whether DNS queries are actually going through the proxy as expected. - Verify with a packet capture:
tcpdump -ni any host TARGET_IPto see where traffic is actually going.
A common misconception:
pinguses ICMP and isn’t suitable for testing an HTTP/SOCKS proxy — always usecurlorncinstead.
6.2 A Decision Cheat Sheet
| Need | Recommended Approach |
|---|---|
A one-off curl or quick test | curl -x |
| A regular CLI tool (Git/pip/npm) | http_proxy / https_proxy environment variables |
| Already have an SSH server, want a quick proxy | ssh -D |
| Set up a stable HTTP proxy | Squid |
| A lightweight multi-protocol proxy | GOST |
| A program without proxy support | ProxyChains (with limitations) |
| SOCKS5 ↔ HTTP conversion | GOST |
| Proxying AI tools like Claude Code | Environment variables, or a GOST reverse proxy |
| A systemd service | systemd’s Environment= |
| Docker pulling images | Configure dockerd (daemon.json) |
| An application inside a container | Pass environment variables via -e |
| Transparent proxying for every program | sing-box TUN (evaluate carefully) |
6.3 Cheat Sheet (Quick-Reference Commands)
# Test an HTTP proxy
curl -x http://127.0.0.1:8080 https://example.com
# SOCKS5 (remote DNS)
curl -x socks5h://127.0.0.1:1080 https://example.com
# Set up SOCKS5 via SSH
ssh -N -D 127.0.0.1:1080 user@server
# Set environment variables in one shot (both cases)
export http_proxy=http://127.0.0.1:8080 https_proxy=http://127.0.0.1:8080 all_proxy=socks5h://127.0.0.1:1080
export HTTP_PROXY="$http_proxy" HTTPS_PROXY="$https_proxy" ALL_PROXY="$all_proxy"
# View proxy variables
env | grep -i proxy
# Clear all proxy variables
unset http_proxy https_proxy all_proxy HTTP_PROXY HTTPS_PROXY ALL_PROXY no_proxy NO_PROXY
# Using ProxyChains
proxychains4 curl https://example.com
# GOST conversion, SOCKS5 → HTTP
gost -L http://127.0.0.1:8080 -F socks5://127.0.0.1:1080
# View listening ports
ss -lntp | grep -E '1080|8080'
# Verbose debugging
curl -v -x socks5h://127.0.0.1:1080 https://example.com
Closing Thoughts
The essence of proxy configuration on Linux is choosing the right layer. From the -x flag at the application layer, to environment variables, to library-function hijacking (ProxyChains), all the way to TUN/TPROXY at the network layer — each layer has its own boundary of applicability. Don’t jump straight for “global proxying.” In most cases, environment variables or an SSH tunnel are already enough. Only reach for a transparent proxy setup, carefully, once you genuinely need “every program, with no awareness required.”