env.dev

HTTP_PROXY

Specifies the proxy server URL for HTTP requests. Used by many command-line tools (curl, wget, apt), package managers (npm, pip), and programming language HTTP clients. The format is typically http://[user:password@]host:port.

Last updated:

HTTP_PROXY points outbound HTTP traffic through a proxy server, in the form http://[user:password@]host:port. It is a de-facto convention rather than a standard, honored by curl, wget, apt, pip, and most language HTTP stacks — but adoption is uneven, which is the whole problem. Pair it with HTTPS_PROXY (for TLS traffic) and NO_PROXY (for exceptions); setting only HTTP_PROXY rarely does what people expect, because most traffic today is HTTPS and ignores it. Both lowercase (http_proxy) and uppercase forms exist, and not every tool reads both.

Provider
General / OS
Category
network
Set by
Set manually or via corporate network configuration
Example
http://proxy.example.com:8080
Gotcha: There is a real security wrinkle: because some CGI environments expose the 'Proxy:' request header as HTTP_PROXY, the curl/libcurl ecosystem deliberately ignores the uppercase HTTP_PROXY in CGI contexts (the 2016 'httpoxy' CVE). For HTTP traffic, prefer lowercase http_proxy. And remember the credentials in the URL are sent in plaintext to the proxy.

How to set HTTP_PROXY

bash (set the trio together)

export HTTP_PROXY=http://proxy.example.com:8080
export HTTPS_PROXY=$HTTP_PROXY
export NO_PROXY=localhost,127.0.0.1,.internal

Docker build args

docker build \
  --build-arg HTTP_PROXY=http://proxy:8080 \
  --build-arg HTTPS_PROXY=http://proxy:8080 -t myapp .

This page covers the whole proxy trio — HTTP_PROXY, HTTPS_PROXY, and NO_PROXY — because no tool reads just one of them, and most proxy debugging sessions end with a mismatch between two of the three.

Why does case matter? The httpoxy story

The lowercase forms (http_proxy) are the original convention, dating back to CERN's libwww in the early 1990s. The uppercase forms came later — and collided with CGI. The CGI spec maps incoming request headers to environment variables as HTTP_*, so a request carrying a Proxy: header materializes HTTP_PROXY inside the handler — attacker-controlled, redirecting the server's own outbound HTTP traffic. That's httpoxy, disclosed in July 2016 as a CVE cluster (CVE-2016-5385 for PHP, CVE-2016-5386 for Go's net/http/cgi, CVE-2016-5387 for Apache httpd, CVE-2016-5388 for Tomcat). The ecosystem's defenses — some predating the disclosure by years — shape behavior today:

  • curl ignores uppercase HTTP_PROXY entirely — by design since April 2001, when curl removed the uppercase lookup after a report that CGI request headers could inject it — fifteen years before httpoxy had a name. It reads lowercase http_proxy only. For the other variables, curl accepts both cases.
  • Go ignores HTTP_PROXY when it detects CGI (the REQUEST_METHOD variable is set).
  • Most other stacks (Python requests, Node's proxy-aware libraries, wget) read both cases, usually preferring lowercase.

The only safe move is to export every variable in both cases:

bash
export http_proxy="http://proxy.corp.example:3128"
export https_proxy="http://proxy.corp.example:3128"
export no_proxy="localhost,127.0.0.1,.internal.example"
export HTTP_PROXY="$http_proxy" HTTPS_PROXY="$https_proxy" NO_PROXY="$no_proxy"

HTTPS_PROXY names the traffic, not the proxy protocol

A recurring misreading: HTTPS_PROXY is the proxy used for https:// requests — the value itself almost always starts with http://. The client connects to the proxy over plain HTTP, issues a CONNECT, and tunnels TLS end-to-end through it, so the proxy never sees decrypted traffic. Proxies that themselves speak TLS (an https:// proxy URL) are real but unevenly supported — curl gained them in 7.52.0 (December 2016), Go in 1.10 — so an https:// scheme in this variable is a compatibility gamble. Also remember that because most traffic is TLS now, setting only HTTP_PROXY and wondering why nothing goes through the proxy is the single most common setup mistake: it is HTTPS_PROXY that carries the bulk of real traffic.

NO_PROXY: the least standardized variable in common use

NO_PROXY is a comma-separated list of hosts that bypass the proxy — and that is where agreement ends. GitLab's engineering blog put it bluntly in 2019 in "We need to talk: Can we standardize NO_PROXY?", and the situation has barely improved:

  • Suffix matching is the lowest common denominator: example.com matches itself and (in most tools) subdomains. A leading dot (.example.com) is accepted by most, required by some, rejected by a few.
  • Wildcards: *.example.com works in some stacks and is treated as a literal in others. A bare * disables proxying in curl, but not everywhere.
  • CIDR ranges (10.0.0.0/8) work in Go's resolver (and therefore kubectl, Docker, Terraform and most Go tooling) but are matched as plain strings by Python's urllib — silently never matching.
  • IPs are not resolved: putting an IP in NO_PROXY does not bypass the proxy for a hostname that resolves to that IP; matching is textual against the request URL.

Practical consequence: write NO_PROXY to the most conservative subset — explicit hostnames and dot-suffixed domains, plus CIDR only when you know every consumer is Go — and always include localhost,127.0.0.1, or health checks and local sidecars start detouring through the corporate proxy.

The tools that don't read any of these

The proxy trio is a userland convention, and notable ecosystems opted out. The JVM ignores it — Java wants -Dhttp.proxyHost / -Dhttps.proxyHost system properties (or java.net.useSystemProxies). The Docker daemon does not inherit your shell's variables — it needs its own configuration (a systemd drop-in, or the proxies key in daemon.json since Docker 23.0), and docker build passes the trio to the build container as predefined build args, which is its own can of worms covered in the Docker environment variables guide. npm reads its own proxy/https-proxy config keys in addition to the environment. When "everything works except this one tool", check whether the tool is on the opt-out list before staring at the variables again.

Debugging a proxy variable problem

bash
# what does the environment actually contain? (both cases)
env | grep -i _proxy

# does curl agree? -v shows CONNECT lines when a proxy is in play
curl -v https://example.com 2>&1 | head -5

# bypass everything for one command to isolate the variable
no_proxy='*' NO_PROXY='*' curl https://internal.example

Keep proxy settings out of dotfiles you share across machines — they are environment-specific by definition, which makes them a textbook case for per-machine .env files rather than committed shell config. Note the password caveat: credentials embedded in a proxy URL travel in cleartext to the proxy and show up in env dumps and debug logs, so treat a credentialed proxy URL with the same care as any other secret.

Frequently Asked Questions

Should I set http_proxy lowercase or HTTP_PROXY uppercase?

Set both to be safe, but prefer lowercase for HTTP traffic: due to the 2016 httpoxy vulnerability, curl and libcurl-based tools ignore the uppercase HTTP_PROXY when running in a CGI context to avoid being hijacked by a client-supplied Proxy header. HTTPS_PROXY and NO_PROXY do not have this caveat.

Was this helpful?

Stay up to date

Get notified about new guides, tools, and cheatsheets.

Browse all 244 environment variables →