🔥 LAUNCH SALE — up to +60% bonus traffic · Residential from $0.67/GB · code BACK15 Claim →
Log in Get proxies
PyProxy is back — buy residential, ISP, mobile & datacenter proxies directly, from $0.67/GB. Launch bonus up to +60%.
Guide · Ubuntu proxy setup

Configuring system-wide proxy settings in Ubuntu

Ubuntu has no single switch that sends every program through a proxy. There are four separate places to set it — the desktop dialogue, the shell environment, apt, and snap — and a handful of programs that ignore all four. Here is each one, in the order that stops you chasing ghosts.

On Ubuntu a proxy is a convention rather than a kernel setting. Nothing intercepts outgoing connections; each program looks somewhere for a proxy address and honours it if it finds one. Command-line tools read the http_proxy and https_proxy environment variables, GUI apps built on GLib read GNOME's own setting, apt reads its own config files, and snap packages read a value stored in snapd. Configuring the system means writing the same address in all four places, and knowing which programs still will not care.

Throughout, the proxy is written as a full URL with credentials, which is what every one of these mechanisms expects:

http://USERNAME:PASSWORD@HOST:PORT

If your password contains @, :, / or #, percent-encode it — p@ss becomes p%40ss — or authentication will fail in a way that looks like a wrong password.

The GNOME network settings dialogue

Open Settings → Network → Network Proxy, switch it from Automatic to Manual and fill in the HTTP and HTTPS host and port. This is the right place to start because it takes effect immediately for GNOME apps, GNOME Software, and Chromium-based browsers, which read the desktop setting rather than the environment.

Two things about that dialogue surprise people. It has no username and password fields, so an authenticated proxy will pop up a credentials prompt per application rather than working silently. And it does not affect terminals you already have open, nor most command-line tools at all.

The same values are stored in gsettings, which is easier to script and to verify:

gsettings set org.gnome.system.proxy mode 'manual'
gsettings set org.gnome.system.proxy.http host 'gw.pyproxy.com'
gsettings set org.gnome.system.proxy.http port 1111
gsettings set org.gnome.system.proxy.https host 'gw.pyproxy.com'
gsettings set org.gnome.system.proxy.https port 1111
gsettings set org.gnome.system.proxy ignore-hosts "['localhost','127.0.0.0/8','::1','192.168.0.0/16']"

# read it back
gsettings list-recursively org.gnome.system.proxy | sort

Leave the SOCKS host empty unless you genuinely have a SOCKS proxy. PyProxy residential traffic is HTTP and HTTPS only, so filling the SOCKS row in with the same gateway details produces connections that fail for no visible reason.

The environment variables, and where to make them persist

This is the layer that matters for real work — curl, wget, git, pip, npm, most Python and Go programs and nearly every container tool read these:

export http_proxy="http://USERNAME-country-us:PASSWORD@gw.pyproxy.com:1111"
export https_proxy="$http_proxy"
export no_proxy="localhost,127.0.0.1,::1,.local"

# uppercase copies, because some tools only read those
export HTTP_PROXY="$http_proxy"
export HTTPS_PROXY="$https_proxy"
export NO_PROXY="$no_proxy"

Note that https_proxy still starts with http://. That is not a typo: the scheme describes how you talk to the proxy, not what the proxy fetches. HTTPS destinations travel through a CONNECT tunnel over that same plain connection, and the TLS session inside it is end-to-end with the target site.

Typed into a shell, those lines die with the shell. To make them permanent, pick the right file:

Every user on the machine: /etc/environment

http_proxy="http://USERNAME-country-us:PASSWORD@gw.pyproxy.com:1111"
https_proxy="http://USERNAME-country-us:PASSWORD@gw.pyproxy.com:1111"
no_proxy="localhost,127.0.0.1,::1,.local"
HTTP_PROXY="http://USERNAME-country-us:PASSWORD@gw.pyproxy.com:1111"
HTTPS_PROXY="http://USERNAME-country-us:PASSWORD@gw.pyproxy.com:1111"
NO_PROXY="localhost,127.0.0.1,::1,.local"

/etc/environment is read by PAM at login, and it is not a shell script. There is no export, no command substitution and no $variable expansion — write every value out in full. Changes apply at the next login, not to the session you are in.

Just your account: ~/.profile

Put the export lines from above in ~/.profile (or ~/.bash_profile). This version is a shell script, so $http_proxy references work. Apply it without logging out using source ~/.profile. Note that ~/.bashrc is read by interactive shells only, so variables set there are missing from scripts run by cron or systemd.

sudo throws your proxy away. For security, sudo resets the environment, so sudo curl … and sudo pip install … go out directly. Use sudo -E to keep the variables, or add Defaults env_keep += "http_proxy https_proxy no_proxy" via sudo visudo. Never put credentials in a sudoers file that others can read.

The no_proxy gotcha that breaks localhost

Set http_proxy without no_proxy and you will break local traffic. A request to http://localhost:8000 is dutifully handed to the proxy, the proxy tries to resolve "localhost" from its own side of the world, and your development server appears to be down. The symptoms are a hanging request, a 502, or a 403 from the proxy — never anything that mentions the proxy by name.

So always set no_proxy, and be precise about its syntax, because it is unusually fussy:

Ports do not take part in matching in most clients, so localhost covers every port on it. To bypass everything for one command, curl --noproxy '*' https://example.com.

apt and snap have their own configuration

apt is the classic "why does everything work except updates" case. It runs under sudo — so your variables are gone — and it reads its own config tree. Create a dedicated file rather than editing apt.conf:

sudo tee /etc/apt/apt.conf.d/95proxies >/dev/null <<'EOF'
Acquire::http::Proxy "http://USERNAME-country-us:PASSWORD@gw.pyproxy.com:1111";
Acquire::https::Proxy "http://USERNAME-country-us:PASSWORD@gw.pyproxy.com:1111";
EOF
sudo chmod 600 /etc/apt/apt.conf.d/95proxies

Semicolons at the end of each line are required. Files in that directory are read in name order, so a numeric prefix keeps yours predictable. To exempt one host — a local mirror, say — add Acquire::http::Proxy::mirror.example.com "DIRECT";. Check the effective value with:

apt-config dump | grep -i proxy

Snap packages run confined and do not inherit your shell environment either, so snapd holds its own copy:

sudo snap set system proxy.http="http://USERNAME-country-us:PASSWORD@gw.pyproxy.com:1111"
sudo snap set system proxy.https="http://USERNAME-country-us:PASSWORD@gw.pyproxy.com:1111"
sudo snap get system proxy

That covers snapd's own downloads. Applications shipped as snaps — Firefox and Chromium on recent Ubuntu releases among them — still keep their internal proxy preferences, which is the next problem.

The programs that ignore all of it

Anything that ships its own HTTP stack has its own switch. This is not Ubuntu being inconsistent; it is simply what happens when a proxy is a convention. The ones that catch people out:

ProgramWhere the proxy actually lives
FirefoxSettings → Network Settings. Defaults to "Use system proxy settings", but a snap build may not see yours.
Docker daemonA systemd drop-in at /etc/systemd/system/docker.service.d/http-proxy.conf, then systemctl daemon-reload. Builds need ~/.docker/config.json as well.
Any systemd serviceEnvironment="http_proxy=…" in a drop-in. Services never read /etc/environment or your profile.
Java-Dhttp.proxyHost, -Dhttp.proxyPort, -Dhttps.proxyHost, -Dhttp.nonProxyHosts.
gitHonours http_proxy for HTTPS remotes, but git@… SSH remotes never use an HTTP proxy. Set git config --global http.proxy to be explicit.
ssh, dig, pingNot HTTP at all. No environment variable applies; use an SSH ProxyCommand, or leave DNS and ICMP direct.

The pattern to remember: environment variables reach HTTP clients and nothing else. UDP, ICMP and raw sockets are unaffected, so a proxy set this way is never a privacy guarantee for the whole machine.

Verifying it, properly

Check the variables exist, then check that traffic really leaves through the proxy:

env | grep -i proxy
curl -s https://httpbin.org/ip
curl -s --noproxy '*' https://httpbin.org/ip

The two curl lines are the actual test. The first should report the proxy's exit address, the second your own — if they match, the proxy is not being used. For the full story on which connection was made, add -v and look for a CONNECT line:

curl -v https://example.com -o /dev/null 2>&1 | grep -iE 'connect|proxy|407'

A 407 means the address and port are right and the credentials are wrong; a hang usually means a firewall. Finish by testing each layer on its own: sudo apt update for apt, snap refresh --list for snap, and a GNOME app for the desktop setting.

All four layers point at one endpoint, so the work above is done once. With a rotating residential gateway the options ride in the username — USERNAME-country-us selects the exit country, and a session token holds one address for about thirty minutes:

curl -x http://USERNAME-country-us:PASSWORD@gw.pyproxy.com:1111 https://httpbin.org/ip

PyProxy sells residential, ISP, mobile and datacenter traffic on one balance, billed per gigabyte with no expiry, so an Ubuntu box configured once keeps working whether you use it today or next month.

PyProxy residential starts at $0.67/GB with traffic that never expires. Link Telegram to a new account and the first gigabyte is free — no card, no deposit.
See proxy plans Get 1 GB free

Questions people ask

Why does apt ignore my proxy when curl works?

Because apt runs under sudo, which strips your environment variables by default, and because apt reads its own configuration. Put the proxy in a file under /etc/apt/apt.conf.d/ using Acquire::http::Proxy and it will work regardless of the shell environment.

Where should I set http_proxy so it survives a reboot?

For every user on the machine, add the variables to /etc/environment, which is read at login by PAM. For your own account only, export them from ~/.profile. Both take effect at the next login rather than immediately, so re-login or source the file.

What is the no_proxy variable for?

It lists destinations that must bypass the proxy. Without localhost, 127.0.0.1 and ::1 in no_proxy, your local development server and any local API call are sent out through the proxy and come back as a timeout or a 502, which looks like a broken application.

Why do some programs ignore the system proxy completely?

Anything with its own HTTP stack has its own setting: Firefox, the Docker daemon, Java, snap-confined apps and many language runtimes never read the shell environment or the GNOME dialogue. Each has to be configured on its own.