Половина моих знакомых разработчиков настраивает прокси в браузере и на этом останавливается. Через день запускают pip install, npm install или docker build - и получают таймаут на двадцать минут. Браузер настройку видит, консоль нет.
Переменные среды - самый короткий путь сказать системе: весь трафик из терминала идет через конкретный узел. Настраивается один раз, подхватывается всеми утилитами, которые умеют читать окружение. А умеют почти все.

Зачем лезть в окружение, если в системе уже что-то настроено
Графическая настройка прокси в Windows или в GNOME работает через WinINET, dconf и прочие прослойки. Их читают браузеры, иногда мессенджеры. Консольные утилиты про эти прослойки ничего не знают: curl, wget, git, pip, npm, apt, dnf, terraform, aws cli смотрят именно на переменные окружения.
Отсюда типичная картина: страницы в браузере открываются, а git clone висит. Или pip качает пакеты напрямую и светит ваш реальный адрес, хотя вы думали, что все уже закрыто. Лечится не переустановкой, а тремя строчками в шелле.
Второй сценарий, где без переменных среды никак - автоматизация: CI-раннеры, контейнеры, systemd-юниты, cron-задачи. Графического интерфейса там нет, есть только окружение процесса. Прописали - работает. Не прописали - падает.
Три переменные, которые закрывают почти все задачи
Основных имен всего три: http_proxy, https_proxy и no_proxy. Первые две задают узел для соответствующих схем, третья перечисляет адреса, которые должны идти напрямую. Для SOCKS5 есть еще all_proxy, о нем отдельно ниже.
Значение собирается всегда одинаково: протокол, логин с паролем через двоеточие и собаку, адрес узла и порт. Формат - scheme://login:password@host:port. Если авторизации нет, часть с логином опускается.
На Linux и macOS используйте строчные имена. Если в окружении окажутся одновременно https_proxy и HTTPS_PROXY с разными значениями, приоритет достанется строчной. Дальше вы будете долго искать, почему настройка "не применилась". На Windows имена регистронезависимы, такой путаницы там нет.
На Reddit в тредах про отладку CI этот конфликт регистра всплывает чаще всего: человек добавил переменную, а приложение читает другую и ходит напрямую. Проверка через env занимает пять секунд и снимает вопрос.
Еще один нюанс, про который вспоминают поздно: переменные среды видны дочерним процессам. Удобно, но означает, что логин с паролем от узла лежит в открытом виде в окружении. Относитесь к этим значениям как к секретам, а не как к безобидной настройке.
Linux и macOS: export на один сеанс терминала
Самый быстрый способ проверить узел - выставить переменные вручную в текущей сессии. В bash и zsh это делает export, по одной строке на переменную. Любой процесс, запущенный из этого же окна, настройку увидит.
Логика простая: export http_proxy со значением для HTTP, export https_proxy для HTTPS, export no_proxy со списком исключений через запятую. В исключения почти всегда попадают localhost, внутренние домены компании и адреса локальных реестров. Иначе запросы к собственной инфраструктуре уйдут во внешний узел и просто не пройдут.
Если в пароле есть собака, двоеточие или решетка, значение берите в одинарные кавычки. Иначе шелл решит, что вы пытаетесь сделать что-то другое, и подставит не то. Проверять результат удобно через echo для каждой переменной: команда покажет ровно то, что увидит приложение.
Снять настройку так же просто: unset для каждой переменной или закрытое окно терминала. Для разовой проверки узла этого достаточно, для постоянной работы - нет.
Чтобы настройка жила после перезагрузки: /etc/environment и profile.d
Временный export умирает вместе с сессией. Для постоянного хранения на Linux есть два основных места, и они не эквивалентны.
/etc/environment - системный файл, который читается при входе в сессию. Формат строгий: имя=значение, без export, без подстановки переменных, без кавычек в привычном понимании. Работает для всех пользователей и для процессов, запущенных не из шелла. Именно этот вариант нужен, когда настройка должна пережить перезагрузку образа или машины.
/etc/profile.d/ - каталог со скриптами, которые подхватываются при старте оболочки. Здесь можно писать нормальный shell-код с export. В дистрибутивах вроде РЕД ОС принято создавать отдельный файл proxy_set.sh и класть туда строки с адресом и портом узла. Плюс в гибкости: условия, проверки, разные узлы для разных групп пользователей.
Для одного пользователя достаточно ~/.bashrc или ~/.zshrc. Самый безопасный вариант, если машиной пользуетесь только вы и не хотите менять поведение системы целиком.
Отдельная история - пакетные менеджеры. dnf, apt и подобные иногда читают собственные конфиги и игнорируют окружение. Если export стоит, а установка пакетов все равно идет напрямую, смотрите конфиг менеджера, а не переменные.
Windows: другой синтаксис и другая логика
На Windows переменные среды живут на трех уровнях: процесса, пользователя и системы. Команда set меняет только текущее окно командной строки и умирает вместе с ним. Команда setx пишет в реестр, то есть на уровень пользователя, и действует для новых процессов. С флагом /M - на уровень системы, но нужны права администратора.
Главная ловушка setx: он не влияет на уже открытое окно. Люди прописывают переменную, тут же запускают проверку в той же консоли, видят старый результат и решают, что команда не работает. Откройте новое окно - и все появится.
Вторая ловушка - ограничение длины значения. setx обрезает длинные строки. Если в no_proxy перечислено два десятка внутренних доменов, хвост списка может тихо отвалиться. В таких случаях надежнее править переменную через свойства системы.
Графический путь тоже никто не отменял: Параметры, Сеть и Интернет, Прокси, ручная настройка, адрес и порт. В старых сборках то же самое лежит в Панели управления через свойства обозревателя, вплоть до отдельных узлов для HTTP, HTTPS, FTP и SOCKS. Только помните: это настройка для приложений WinINET, консольные утилиты ее не видят. Для них все равно нужны переменные среды.
Грабли, на которые наступают даже опытные
Конфликт регистра. Оставили в системе HTTPS_PROXY, добавили https_proxy - получили непредсказуемое поведение, зависящее от приложения. Держите один вариант, желательно строчный.
Лишний слэш в конце значения. Часть библиотек переваривает завершающий слеш спокойно, часть начинает клеить URL неправильно и выдает ошибку вида "не удалось разрешить имя". Убирайте слэш, если не уверены.
Спецсимволы в пароле. Собака, двоеточие, решетка, процент - все это ломает разбор строки. Либо экранируйте через процентное кодирование, либо заведите отдельного пользователя узла с простым паролем. Второе честнее.
Утечка в репозиторий. Файл .env с логином и паролем от узла, закомментированный export в README, скриншот в чате - классика. Переменные среды удобны, но секретом от этого не становятся.
Забытый no_proxy. Внутренний реестр пакетов, локальная база, служебный API на соседней машине - все уходит во внешний узел и не отвечает. Список исключений пишите сразу, а не после первого инцидента.
no_proxy: список, который экономит часы отладки
Значение no_proxy - перечисление через запятую. Туда логично добавить localhost, домены внутренней зоны, адреса локальных реестров и все, что должно ходить напрямую. Поддержка шаблонов и CIDR-диапазонов зависит от библиотеки: curl, Go и Java понимают это по-разному. Универсальности ждать не стоит.
Практическое правило, которое у меня сложилось: если сервис внутри вашей сети, он почти всегда должен быть в no_proxy. Внешний трафик - через узел, внутренний - напрямую. Скорость выше, точек отказа меньше.
Отдельно про IP в этом списке. Если внутренние сервисы адресуются по именам, пишите имена. Смешивать оба варианта стоит только когда действительно нужно.
SOCKS5 и MTProto: где переменных среды недостаточно
Для SOCKS5 есть переменная all_proxy. Часть инструментов ее понимает, часть игнорирует и требует флаг в командной строке. Причина архитектурная: SOCKS5 работает на уровне сессии и не использует HTTP-метод CONNECT. Приложение должно явно уметь с ним разговаривать. curl умеет, старые сборки чего-нибудь экзотического - нет.
MTProto устроен еще независимее. Это не прокси в привычном смысле, а транспорт внутри протокола мессенджера, с собственным шифрованием и рукопожатием. Переменные среды на него не влияют вообще. Настройка делается в клиенте, в разделе сети и данных, где вы указываете узел и порт. Ждать, что export подействует на мессенджер, бессмысленно.
HTTP-прокси живет на уровне запроса, SOCKS5 на уровне сессии, MTProto вообще внутри приложения. Переменные среды - универсальный ключ только к первым двум, и то не всегда.
Docker, CI и сервисы: там переменные среды - единственный путь
В контейнерах окружение хоста не наследуется автоматически. При сборке образа переменные прокидывают явно, через аргументы сборки или через файл с настройками демона. Иначе сборка пойдет напрямую, даже если в вашем терминале все прекрасно работает.
В CI-системах переменные задаются на уровне workflow или секретов репозитория. Документация GitHub по раннерам прямо говорит: относитесь к этим значениям как к конфиденциальным, независимо от поведения оболочки. Там же совет использовать setting-scope=machine при сборке кастомного образа, чтобы настройка пережила перезагрузку виртуальной машины.
В systemd настройка задается в юните директивой Environment или отдельным файлом окружения. Процесс, запущенный службой, не увидит ваш ~/.bashrc, сколько бы вы туда ни писали. Это, пожалуй, самая частая причина ситуации "настройка есть, а сервис ходит напрямую".
Как проверить, что все сработало
Сначала проверьте сами переменные: env в Linux и macOS, set без аргументов в Windows. Убедитесь, что там ровно то, что ожидали, без опечаток и лишних слэшей.
Потом сделайте реальный запрос через curl с флагом verbose и посмотрите на строку подключения. В логе видно, идет соединение через узел или напрямую. Это надежнее любых догадок.
Третий шаг - проверка с точки зрения приложения. Запустите ту самую команду, которая раньше висела: pip install, git clone, docker pull. Прошла - настройка жива. Не прошла - вернитесь к пункту про регистр и no_proxy, в девяти случаях из десяти дело там.
Где брать узлы под такие задачи
Для терминала и CI нужны стабильные адреса с понятной географией и без сюрпризов в виде смены выходного узла посреди сессии. Резидентные хороши для сбора данных и проверки выдачи, серверные - для скорости и больших объемов. Под автоматизацию почти всегда берут второй вариант: дешевле и предсказуемее.
Себе и клиентам я подбираю узлы в каталоге 3seller.com/proxy. Там есть фильтры по стране, типу и протоколу, и сразу видно, поддерживается ли авторизация по логину и паролю. Для строки вида scheme://login:password@host:port это ровно то, что нужно. Без авторизации переменные среды настраивать неудобно и небезопасно: любой, кто узнал адрес, пользуется вашим каналом.
При выборе смотрите на три вещи. Первая - стабильность сессии: если узел меняется каждые тридцать секунд, половина скриптов будет падать на авторизации. Вторая - поддержка HTTPS через CONNECT, без нее https_proxy работать не будет. Третья - понятный лимит по трафику, иначе ночной парсинг съест месячный пакет за одну ночь.
Географию берите с запасом. Один и тот же запрос из разных стран отдает разные результаты, и это нормальный рабочий инструмент, а не хитрость.
Пара слов про безопасность, которые обычно пропускают
Логин и пароль в переменных среды видны любому процессу, запущенному от того же пользователя. На общем сервере это плохая идея. Если машиной пользуется несколько человек, заведите отдельного пользователя под задачи, которым нужен узел, и держите настройки в его профиле.
Не храните учетные данные в /etc/environment на машинах, к которым есть доступ у подрядчиков. Этот файл читается всеми. Для чувствительных случаев лучше отдельный файл с ограниченными правами и подключение через профиль оболочки.
И проверяйте, куда уходит трафик после настройки. Один тестовый запрос к сервису, показывающему ваш адрес, скажет больше, чем час чтения конфигов.
Что сделать прямо сейчас
Откройте терминал и выставите три переменные для своей машины. Проверьте через curl. Работает - перенесите строки в постоянный файл, чтобы не повторять каждый раз. Не работает - начните с регистра и формата значения, а не с переустановки утилит.
Дальше пройдитесь по инструментам, которыми пользуетесь ежедневно, и убедитесь, что каждый видит настройку. Git, пакетный менеджер, Docker, CI. Обычно находится одно-два забытых места, и именно они портят всю картину. Следующий шаг простой: добавьте в no_proxy адрес вашего внутреннего реестра, пока он снова не сломал сборку.
Узлы под это берите осознанно, с авторизацией и стабильной сессией - в каталоге 3seller.com/proxy есть из чего выбрать под разные задачи, от разовых проверок до постоянной работы парсеров.