Linux doesn't have one proxy switch. It has three layers that don't know about each other, and most of the confusion around proxies on Linux comes from changing one layer and expecting another to notice.
The three places a proxy setting can live
- 1
The desktop settings panel
GNOME or KDE. Reaches graphical applications and browsers. Terminal programs mostly ignore it.
- 2
Environment variables
http_proxyand friends. The real Linux answer: works on a server with no desktop, and it's what curl, wget and most command line software read. - 3
The application's own config
apt, Docker, snap, git and systemd each keep their own proxy setting and read nothing else. This is where people get stuck.
Work out which layer you need before you start, because setting the wrong one produces a proxy that appears to do nothing at all.
Environment variables, the way that works everywhere
Open a terminal and set two variables. For a proxy with a login:
export http_proxy="http://hp_ir4k2:9fa2c1@198.51.100.7:8080"
export https_proxy="http://hp_ir4k2:9fa2c1@198.51.100.7:8080"
For an open proxy with no login, drop the credentials:
export http_proxy="http://203.0.113.42:3128"
export https_proxy="http://203.0.113.42:3128"
Both variables get the same value, and both start with http://. That's correct even for https_proxy, because the scheme describes how you talk to the proxy, and that conversation is plain HTTP. Writing https:// there is one of the most common Linux proxy mistakes and usually fails to connect at all.
The user:pass@host:port arrangement is the URL shape. If your provider gave you the colon-separated shape instead, the four values are the same and only the order changes, which the proxy format guide walks through in detail.
Set both lowercase and uppercase if you want maximum coverage. Different programs read different ones, and there's no rule about which:
export HTTP_PROXY="$http_proxy"
export HTTPS_PROXY="$https_proxy"
Exempting hosts with no_proxy
Anything listed in no_proxy bypasses the proxy entirely:
export no_proxy="localhost,127.0.0.1,::1,.internal.example.com"
Worth setting on any machine that talks to itself or to a local network. Without it, a request to localhost goes out to the proxy and comes back refused, which looks like your own service has broken.
A password with @ or : in it breaks the URL
The URL form uses @ to separate the login from the host and : to separate the fields. A password containing either splits the URL in the wrong place. Percent-encode them: @ becomes %40, : becomes %3A, / becomes %2F. The password is right and the URL is wrong, which is why this is so hard to spot.
Making it stick
Those exports last until you close the terminal.
For your own account, add the same lines to the end of ~/.bashrc, or ~/.zshrc on zsh. Open a new terminal afterwards, since the file is only read when a shell starts.
For every user on the machine, use /etc/environment:
http_proxy="http://hp_ir4k2:9fa2c1@198.51.100.7:8080"
https_proxy="http://hp_ir4k2:9fa2c1@198.51.100.7:8080"
no_proxy="localhost,127.0.0.1,::1"
That file is not a shell script. No export, no $VAR expansion, no comments after values. Log out and back in for it to apply.
Why sudo apt update ignores all of it
This is the single most common Linux proxy problem, and the cause is not apt.
sudo deliberately throws your environment away. Nearly every distribution ships with env_reset enabled in /etc/sudoers, so the variables you exported never reach the command running as root. Your proxy works perfectly as your own user and appears to vanish the moment you type sudo.
Two fixes. The quick one:
sudo -E apt update
-E carries your environment through. It works, and it needs remembering every single time.
The durable one is to give apt its own config, which is what you want on a machine that always goes through a proxy. Create /etc/apt/apt.conf.d/95proxies:
Acquire::http::Proxy "http://hp_ir4k2:9fa2c1@198.51.100.7:8080";
Acquire::https::Proxy "http://hp_ir4k2:9fa2c1@198.51.100.7:8080";
Note the semicolons. apt's config format wants them and fails oddly without them. Confirm apt has actually picked it up with:
apt-config dump | grep -i proxy
If that prints your proxy back, apt is configured regardless of what any environment variable says.
The desktop setting
On GNOME (Ubuntu's default), open Settings, then Network, then the gear next to Network Proxy. Switch it to Manual and fill in the HTTP Proxy and HTTPS Proxy rows with the host in one box and the port in the other. GNOME gives you a separate box for each, so the port never belongs in the address field.
The same thing from a terminal, which is handy in a script:
gsettings set org.gnome.system.proxy mode 'manual'
gsettings set org.gnome.system.proxy.http host '198.51.100.7'
gsettings set org.gnome.system.proxy.http port 8080
gsettings set org.gnome.system.proxy.https host '198.51.100.7'
gsettings set org.gnome.system.proxy.https port 8080
Turn it off again with gsettings set org.gnome.system.proxy mode 'none'.
On KDE Plasma, it's System Settings, then Network, then Settings, then Proxy. Pick "Use manually specified proxy configuration" and fill in the same values.
Two limits are worth knowing before you rely on either. GNOME's own credential handling is inconsistent across versions, so an authenticated proxy often prompts rather than storing the login. And a terminal you opened before changing the setting keeps the old environment, so a quick test in an existing window can report failure for a setting that's working fine.
Software that needs telling separately
Each of these reads its own config and nothing else
| Program | Where its proxy setting lives |
|---|---|
| apt | /etc/apt/apt.conf.d/95proxies, as above |
| Docker daemon | /etc/systemd/system/docker.service.d/http-proxy.conf, then systemctl daemon-reload and restart Docker |
| Docker builds and containers | ~/.docker/config.json, which is separate from the daemon's own setting |
| snap | sudo snap set system proxy.http="http://user:pass@host:port", and the same for proxy.https |
| git | git config --global http.proxy http://user:pass@host:port |
| A systemd service | An Environment="http_proxy=http://user:pass@host:port" line in the unit, since services never read your shell profile |
| curl and wget | Read the environment variables, and also ~/.curlrc and ~/.wgetrc if you prefer a file |
Standard locations on Debian and Ubuntu. Fedora and Arch use the same ones apart from apt.
The pattern behind the table: anything that runs as a system service starts outside your login session, so it never sees your shell profile. That's why Docker and systemd units need the setting written into their own configuration rather than exported in a terminal.
Check which proxy is actually in use
Print what your shell currently has:
env | grep -i proxy
Then prove it end to end by asking a site what IP it sees:
curl -s https://api.ipify.org
If that prints your proxy's address instead of your own, everything above worked. If it prints your own address, the request went direct, and the variables either aren't set in this shell or the program ignored them.
To test a proxy without changing anything at all, pass it to curl for one command:
curl -x "http://hp_ir4k2:9fa2c1@198.51.100.7:8080" -s https://api.ipify.org
-x overrides everything, so this is the cleanest way to separate "the proxy is dead" from "my configuration isn't being read". Our proxy checker does the same job from a browser and also reports which protocols the proxy speaks and how fast it answers.
SOCKS5 on Linux
curl understands SOCKS proxies directly:
curl -x "socks5h://user:pass@198.51.100.7:1080" -s https://api.ipify.org
The trailing h in socks5h means the proxy resolves the hostname rather than your machine doing it. That's usually what you want: resolving locally leaks the lookup to your own DNS and can send you to a server in the wrong region.
Plenty of other software silently ignores a socks5:// value in http_proxy and connects directly, which is a failure mode with no error message. For those, either use proxychains, which forces a program through a SOCKS proxy whether it wants to or not, or run a small local bridge that accepts plain HTTP and speaks SOCKS5 upstream.
Turning it all off
Undo each layer separately, because they're independent:
unset http_proxy https_proxy HTTP_PROXY HTTPS_PROXY no_proxy
Then remove the lines from ~/.bashrc or /etc/environment if you added them, delete /etc/apt/apt.conf.d/95proxies if you created it, and set the desktop mode back to none. Forgetting one is the usual reason a machine keeps trying to reach a proxy that's long gone, and the symptom is a system that seems to have lost its internet connection entirely.