Tutorial

How to Set Up a Proxy on Linux

Linux has no single proxy switch. This covers the desktop setting, the environment variables that work everywhere, why sudo apt ignores them, and how to check which one is actually in use.

HProxy Team · ·6 min read
HProxy. Tutorial

Free proxies won't hold up here.

Shared datacenter IPs get flagged and dropped fast. When it has to hold, gaming, streaming, accounts, you need mobile and residential IPs that read as a real device, from $0.44/GB, pay as you go.

See plans & pricing

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. 1

    The desktop settings panel

    GNOME or KDE. Reaches graphical applications and browsers. Terminal programs mostly ignore it.

  2. 2

    Environment variables

    http_proxy and 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. 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

ProgramWhere 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
snapsudo snap set system proxy.http="http://user:pass@host:port", and the same for proxy.https
gitgit config --global http.proxy http://user:pass@host:port
A systemd serviceAn Environment="http_proxy=http://user:pass@host:port" line in the unit, since services never read your shell profile
curl and wgetRead 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.

Frequently asked questions

What's the command to set a proxy on Linux?
export http_proxy="http://user:pass@198.51.100.7:8080" and the same again for https_proxy. That covers curl, wget, apt when it's run without sudo, most package managers and a large amount of command line software. It only applies to the terminal you typed it in, and it disappears when you close that terminal.
Why does sudo apt update ignore my proxy?
Because sudo throws your environment away by default. The env_reset option is on in almost every distribution, so http_proxy never reaches the command being run as root. Either use sudo -E to carry the environment through, or give apt its own config file in /etc/apt/apt.conf.d/, which is the more reliable fix.
Does the GNOME proxy setting cover everything?
No. It covers GNOME applications and most browsers, and a lot of software reads it indirectly. It does not reliably reach terminal programs, and it doesn't touch apt, Docker or snap. Terminals started before you changed the setting definitely won't see it.
How do I set a proxy permanently on Linux?
For your own user, add the export lines to ~/.bashrc or ~/.zshrc. For every user on the machine, put the variables in /etc/environment, one KEY=value per line with no export keyword, since that file is not a shell script. Both need a new login or a new shell before they take effect.
My password has an @ in it and the proxy stops working. Why?
In the URL form, @ separates the login from the host, so a password containing one splits the URL in the wrong place. Percent-encode it: @ becomes %40, a colon becomes %3A and a slash becomes %2F. This catches people constantly because the password is correct and the URL is not.
How do I turn the proxy off again?
unset http_proxy https_proxy no_proxy in the current shell, and delete the lines from ~/.bashrc or /etc/environment if you made it permanent. If you also set it in the desktop settings or in an apt config file, those are separate and have to be turned off separately.
Which is better on Linux, environment variables or the desktop setting?
Environment variables, for almost everything. They work on servers with no desktop at all, they're visible and greppable, and they're what command line software actually reads. Use the desktop setting when you want browsers and graphical apps proxied and you're not working in a terminal.
Can I use a SOCKS5 proxy with these variables?
Sometimes. curl accepts socks5:// and socks5h:// in http_proxy, and so do a few other tools, but plenty of software silently ignores a SOCKS URL there and connects directly. For anything that only understands HTTP proxies, run a local bridge that speaks SOCKS5 upstream and plain HTTP to your software.

Proxies that don't die mid-job

Residential, ISP, datacenter and mobile, verified by the same engine that runs tens of millions of checks. They read as a real device and hold up under load. Pay as you go, and your balance never expires. $0.44/GB is the 2,000 GB+ rate; a single gigabyte is $0.50/GB, with no minimum order.

47M+ proxy checks run · 100+ countries · HTTP / HTTPS / SOCKS · re-checked every few minutes · no signup