РуководстваОпубликовано: 06.11.2026• 0 просмотров

curl подключается, а Python API — нет: прокси HTTPX, переменные окружения и NO_PROXY

Проверьте приоритет прокси окружения, явного proxy и NO_PROXY в HTTPX, различайте протокол цели, протокол прокси и источник CA; локальные сервисы с маркерами помогут проверить маршрут без платных API.

Если на одном компьютере curl получает HTTP-ответ, а Python сообщает об отказе в соединении, ошибке прокси или проверке сертификата, сначала сравните фактический маршрут запросов. Браузер, терминал, IDE, фоновая служба и контейнер могут использовать разные настройки. Доступная главная страница также не доказывает работоспособность вызова модели.

Статья относится к стандартной конфигурации транспорта Python HTTPX 0.28.1; источники проверены 6 ноября 2026 года. Собственные transport, mounts и обёртки SDK могут изменить маршрутизацию: проверяйте место создания реального клиента. Эксперимент ниже обращается только к локальному адресу обратной связи, не передаёт API Key и не вызывает платные модели. Его результаты не являются оценкой провайдера.

1. Сначала сравните условия проверки

Запишите для curl и Python протокол, хост, порт, путь и HTTP-метод. Добавьте место запуска, версии, способ запуска процесса, настройку перехода по редиректам и источник прокси: окружение или код. Сравнивайте один и тот же заранее проверенный, разрешённый диагностический адрес без учётных данных. Не используйте повторные генерации для поиска сетевой ошибки.

НаблюдениеЧто оно показываетЧего оно не доказывает
curl получает 401 или 404, HTTPX не получает HTTP-статусЗапросы дошли до разных этаповЧто curl проверил модель или проблема именно в API Key
HTTPX получает отказ в соединении, в настройках есть локальный порт проксиНужно проверить адрес и слушающий порт локального проксиЧто провайдер недоступен
HTTPX сообщает ProxyError или при взаимодействии с прокси появляется 407Нужно проверить установление соединения, аутентификацию и правила проксиЧто поможет замена ключа API модели
HTTPX не может проверить сертификатНужно проверить фактического собеседника, доверенные CA и корпоративные настройкиЧто следует отключить TLS-проверку
Из терминала работает, из службы — нетОкружение при запуске может различатьсяЧто все процессы машины видят последние настройки

Сохраняйте тип исключения после удаления чувствительных данных и время события. Не копируйте целиком исключения и отладочные журналы: они могут содержать URL прокси с паролем. Ошибка описывает отдельную попытку и не доказывает, что задача целиком не повторялась или не будет оплачена. Для обращения в поддержку пригодится шаблон минимального воспроизведения и обезличенного обращения.

2. Разделяйте протокол цели и протокол прокси

HTTPX по умолчанию использует настройки окружения. HTTP_PROXY задаёт прокси для HTTP-целей, HTTPS_PROXY — для HTTPS-целей, ALL_PROXY может задавать общий прокси, а NO_PROXY определяет цели, которые не должны проходить через прокси из окружения.[1]

Значение HTTPS_PROXY не обязано начинаться с https://. HTTPS в адресе API описывает защищённое соединение с целевым сервером, а схема URL прокси — способ соединения с самим прокси. При обычном HTTP-прокси доступ к HTTPS-цели обычно начинается с создания туннеля CONNECT; затем клиент выполняет TLS-рукопожатие с целью внутри туннеля. Поэтому адрес прокси часто имеет вид http://хост-прокси:порт.[2] Используйте протокол, подтверждённый администратором или документацией прокси, а не меняйте схему только из-за HTTPS у цели.

Проверьте и сетевое окружение адреса. В контейнере 127.0.0.1 обозначает сам контейнер, а на удалённом сервере — этот сервер. Ни один из этих адресов не указывает автоматически на прокси вашего ноутбука. Популярный номер порта не означает, что нужная служба действительно там запущена.

Учётные данные прокси и API Key — разные секреты. URL прокси может включать имя пользователя и пароль: не публикуйте полное значение в обращениях, снимках экрана или репозитории. Локально проверьте наличие настройки, протокол и обезличенный адрес, а способ аутентификации уточните через установленный безопасный канал.

3. Наличие переменной ещё не означает, что клиент её использует

Для обычного Client в HTTPX 0.28.1 исходный код задаёт следующие отношения между настройками:[3]

Способ создания или настройкаКак это понимать
proxy не передан, используется стандартный trust_env=TrueЧитаются прокси окружения и их правила NO_PROXY
proxy=None при стандартном trust_env=TrueПрокси окружения всё ещё может использоваться; это не принудительное прямое соединение
proxy явно содержит адресИспользуется явный прокси; исключения NO_PROXY из окружения автоматически не объединяются с ним
trust_env=False без явного прокси и собственной маршрутизацииПрокси окружения не используются
trust_env=False вместе с явным proxyЯвный прокси остаётся действующим
Собственный transport или mountsНужна отдельная проверка: таблица стандартной маршрутизации недостаточна

HTTPX строит маршруты прокси при создании клиента. После изменения NO_PROXY в том же процессе не следует ожидать, что существующий Client автоматически перечитает настройки. Завершите старые запросы, закройте старый клиент и создайте новый с проверкой конфигурации. Изменение файла для службы или контейнера также не означает, что настройки уже работающего процесса изменились. Не закрывайте общий клиент с производственными запросами только ради диагностики.

Кроме того, HTTPX 0.28.1 получает сведения о прокси через urllib.request.getproxies из Python. При определённых условиях в Windows и macOS возможен переход к системным настройкам.[4] Поэтому отсутствие перечисленных переменных ещё не доказывает прямое соединение. И наоборот, само наличие переменной не подтверждает итоговый маршрут: сопоставляйте явные параметры и фактические записи прокси.

У curl собственные правила и источники конфигурации. Например, официальная документация задаёт строчное имя http_proxy и отдельно описывает правила доменов и CIDR для NO_PROXY.[6] В Windows имена переменных окружения обычно нечувствительны к регистру: не переносите туда эксперименты с регистром из Unix. Также не считайте, что шаблоны, начальная точка, CIDR или запись порта имеют одинаковое значение во всех клиентах. Сначала сравнивайте узкие исключения с точным именем хоста или IP; отдельно проверяйте localhost и 127.0.0.1.

4. Проверьте четыре маршрута на локальных сервисах

Для скрипта нужен установленный HTTPX; фактически проверена версия 0.28.1. Сохраните код как proxy_demo.py и запустите интерпретатором Python с этой версией. Скрипт создаёт два сервиса на случайных портах только интерфейса 127.0.0.1. Первый возвращает origin. Второй играет роль входа прокси и возвращает proxy, ничего никуда не пересылая.

Скрипт временно заменяет связанные настройки только в собственном отдельном процессе и восстанавливает их при завершении или исключении. Не встраивайте такой эксперимент с изменением окружения в многопоточную производственную службу. У запросов установлен тайм-аут 2 секунды для отдельных этапов, переходы по редиректам отключены, принимается не более 32 байт ответа, после чего ответ закрывается. Этот тайм-аут не является строгим общим бюджетом запроса, а предел байт не ограничивает память низкоуровневых сетевых буферов.

import os
from contextlib import contextmanager
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from threading import Thread
import httpx

NAMES = {"http_proxy", "https_proxy", "all_proxy", "no_proxy",
         "ssl_cert_file", "ssl_cert_dir", "request_method"}

@contextmanager
def isolated_env(proxy):
    saved = {k: v for k, v in os.environ.items() if k.lower() in NAMES}
    try:
        for k in list(os.environ):
            if k.lower() in NAMES:
                del os.environ[k]
        for k in ("HTTP_PROXY", "HTTPS_PROXY", "ALL_PROXY"):
            os.environ[k] = proxy
        os.environ["NO_PROXY"] = ""
        yield
    finally:
        for k in list(os.environ):
            if k.lower() in NAMES:
                del os.environ[k]
        os.environ.update(saved)

@contextmanager
def endpoint(marker):
    class Handler(BaseHTTPRequestHandler):
        def setup(self):
            super().setup()
            self.connection.settimeout(2)
        def do_GET(self):
            body = marker.encode("ascii")
            self.send_response(200)
            self.send_header("Content-Length", str(len(body)))
            self.send_header("Connection", "close")
            self.end_headers()
            self.wfile.write(body)
        def log_message(self, *_):
            pass
    server = ThreadingHTTPServer(("127.0.0.1", 0), Handler)
    thread = Thread(target=server.serve_forever, daemon=True)
    thread.start()
    try:
        yield f"http://127.0.0.1:{server.server_port}"
    finally:
        server.shutdown()
        server.server_close()
        thread.join(timeout=3)

def read_marker(client, url):
    with client.stream("GET", url) as response:
        response.raise_for_status()
        data = bytearray()
        for chunk in response.iter_raw():
            if len(data) + len(chunk) > 32:
                raise ValueError("Unexpected response size")
            data.extend(chunk)
        return data.decode("ascii")

def main():
    with endpoint("origin") as origin, endpoint("proxy") as proxy:
        with isolated_env(proxy):
            cases = [
                ("env", "", {}, "proxy"),
                ("no_proxy", "127.0.0.1", {}, "origin"),
                ("explicit_proxy", "127.0.0.1", {"proxy": proxy}, "proxy"),
                ("ignore_env", "", {"trust_env": False}, "origin"),
            ]
            for label, bypass, options, expected in cases:
                os.environ["NO_PROXY"] = bypass
                with httpx.Client(timeout=2, follow_redirects=False,
                                  **options) as client:
                    actual = read_marker(client, origin)
                if actual != expected:
                    raise AssertionError(f"{label}: {actual} != {expected}")
                print(label, actual)

if __name__ == "__main__":
    main()

Ожидаются четыре строки: env proxy, no_proxy origin, explicit_proxy proxy, ignore_env origin. Особенно важна третья: при явно переданном proxy переменная NO_PROXY не переводит цель на прямое соединение автоматически.

Дополнительные локальные проверки подтвердили сохранение старого маршрута существующим клиентом и чтение новых настроек новым клиентом; использование окружения при proxy=None; сохранение явного прокси при trust_env=False; границу 32/33 байта и восстановление окружения после исключения. Всего прошли 13 проверок. Проверка сертификатов касалась только чтения несуществующего пути CA при создании клиента.

Эксперимент не реализует HTTPS CONNECT, TLS-рукопожатие, SOCKS, аутентификацию прокси или реальную пересылку. Он также не проверяет сетевой маршрут curl. Результат показывает лишь выбор локального входа данной версией HTTPX в контролируемых условиях и не доказывает исправление корпоративного прокси или реального API.

5. trust_env=False меняет не только использование прокси

В исходном коде create_ssl_context для HTTPX 0.28.1 стандартная ветка verify=True при trust_env=True сначала проверяет SSL_CERT_FILE, затем SSL_CERT_DIR. При trust_env=False эти источники окружения пропускаются и используется стандартный набор CA из certifi.[5] Таким образом, изменение trust_env может одновременно изменить прокси и источник доверенных CA. Нельзя объяснять исчезнувшую или появившуюся ошибку сертификата исключительно сменой маршрута.

Это не отключение проверки сертификата. verify=False — отдельное опасное изменение, которое в этой инструкции не требуется. Если корпоративная среда использует управляемые CA, сопровождающий должен подтвердить доверенный источник сертификатов и при необходимости явно передать корректный SSLContext, сохранив проверку имени хоста и цепочки. Поведение уже переданного SSLContext нельзя выводить из стандартной ветки.

При проверке обнаружилось расхождение: текст старой SSL-документации под тегом 0.28.1 об автоматическом чтении переменных сертификатов не соответствует исходному коду под тем же тегом. Вывод этого раздела ограничен проверенным кодом и локальным результатом создания клиента; он не переносится на другие версии. Истёкшие сертификаты, несовпадение имени и отсутствие CA разобраны в инструкции по ошибкам SSL.

6. Завершите проверку в исходном окружении сбоя

  1. Подтвердите разрешённый организацией способ выхода в сеть и ответственный источник прокси: окружение запуска, настройки приложения или явный код. Меняйте только необходимые параметры, не заменяя одновременно прокси, сертификаты, модель и ключ.
  2. Пусть сопровождающий проверит настройки реального процесса. Не выгружайте всё окружение: достаточно наличия нужных переменных, обезличенных адресов, совпадения цели с NO_PROXY, версии клиента и времени вступления настроек в силу.
  3. Для внутренних целей, которым требуется прямой доступ, задавайте узкие исключения и проверяйте их. NO_PROXY=* резко расширяет обход прокси и не подходит как универсальное производственное исправление. Для явного прокси исключения нужно настраивать средствами маршрутизации приложения, а не только добавлением переменной окружения.
  4. Примените настройки по процедуре развёртывания и пересоздайте клиент в исходной службе или контейнере. Проверьте тот же адрес без учётных данных, тем же методом и без автоматических редиректов. Запишите HTTP-статус или этап сбоя; в разрешённых пределах сопоставьте результат с журналом прокси.
  5. После восстановления сети и проверки сертификатов ответственная за приложение сторона должна проверить необходимые задачи модели с ограниченными тестовыми учётными данными и бюджетом. HTTP-ответ без авторизации и локальный маркер не заменяют проверку прав, завершения потока и начислений.

Если маршрут подтверждён, но при параллельной работе остаётся PoolTimeout, перейдите к проверке пула HTTPX и освобождения потоковых ответов. Дальнейшая смена переменных прокси не устраняет ожидание свободного соединения в пуле.

7. Источники и границы применимости

Первоисточники проверены 6 ноября 2026 года. Материалы с фиксированной версией описывают HTTPX 0.28.1. Документация curl приведена для пояснения различий клиентов; её правила сопоставления не перенесены на HTTPX.

Опубликовано: 6 ноября 2026 г.
最后更新: 06.11.2026

Похожие статьи

Руководства

Длинный ID из API изменился во фронтенде? Точность JavaScript и передача строк

Изменились последние цифры ID, поиск вернул 404 или номера в счёте слились? Проверьте текст JSON, разбор, хранение и повторную отправку; узнайте, почему String и BigInt иногда уже не помогут, и проверьте передачу строковых ID на локальных примерах.

Руководства

Как считать стабильность API: исходные запросы, повторы и конечная успешность

Успех после повтора нельзя считать новым независимым успехом. Статья разделяет request_id, попытки и конечные состояния, объясняет границы 429, 503, тайм-аутов и идемпотентности, а локальный скрипт считает конечную успешность и усиление повторами.

Руководства

API вернул 202 без результата: состояния асинхронных заданий, опрос и повторная отправка

Различайте приём задания, успешную проверку статуса и завершение работы. Сохраняйте ID, ограничивайте опрос и разбирайте 404, истечение срока результата и неизвестный исход отправки без повторного создания задания.

Отказ от ответственности

Данные о сервисах и ценах вводятся вручную; доступность сайтов проверяется автоматически с указанием времени последней проверки.Информация о провайдерах может меняться, поэтому перед оплатой рекомендуем посетить официальный сайт сервиса. Мы не гарантируем качество услуг сторонних провайдеров.

© 2026 Выбор API. All rights reserved.