Назад к блогу

Почему прокси работает в браузере, но не в коде: полный разбор проблемы

Прокси отлично работает в браузере, но скрипт возвращает ошибки? Разбираем типичные причины и даём готовые решения для Python, Node.js и cURL.

📅11 декабря 2025 г.

Почему прокси работает в браузере, но не в коде: полный разбор проблемы

Классическая ситуация: настроили прокси в браузере, открыли сайт — всё работает. Запустили скрипт с тем же прокси — ошибка подключения, таймаут или бан. Разбираемся, почему так происходит и как это исправить.

Чем запрос из браузера отличается от запроса из кода

Когда вы открываете сайт в браузере через прокси, происходит гораздо больше, чем просто HTTP-запрос. Браузер автоматически:

  • Отправляет полный набор заголовков (User-Agent, Accept, Accept-Language, Accept-Encoding)
  • Выполняет TLS-handshake с корректным набором шифров
  • Обрабатывает редиректы и cookies
  • Выполняет JavaScript и загружает зависимые ресурсы
  • Кэширует DNS-ответы и сертификаты

Минимальный запрос из кода выглядит для сервера совершенно иначе — как робот, а не человек. Даже если прокси работает корректно, целевой сайт может блокировать именно ваш скрипт.

Проблемы с аутентификацией прокси

Самая частая причина — неправильная передача логина и пароля. Браузер показывает всплывающее окно для ввода credentials, а в коде это нужно делать явно.

Неправильный формат URL

Частая ошибка — пропуск схемы или неправильное экранирование спецсимволов:

# Неправильно
proxy = "user:pass@proxy.example.com:8080"

# Правильно
proxy = "http://user:pass@proxy.example.com:8080"

# Если в пароле есть спецсимволы (@, :, /)
from urllib.parse import quote
password = quote("p@ss:word/123", safe="")
proxy = f"http://user:{password}@proxy.example.com:8080"

Аутентификация по IP vs логин/пароль

Некоторые прокси-провайдеры используют whitelist по IP-адресу. Браузер на вашем компьютере работает, потому что ваш IP добавлен в whitelist. А скрипт на сервере — нет, потому что у сервера другой IP.

Проверьте в панели провайдера, какой метод аутентификации используется и какие IP добавлены в whitelist.

Несовпадение протоколов HTTP/HTTPS/SOCKS

Браузер часто автоматически определяет тип прокси. В коде нужно указывать явно, и ошибка в протоколе приводит к молчаливому отказу.

Тип прокси Схема в URL Особенности
HTTP прокси http:// Работает для HTTP и HTTPS через CONNECT
HTTPS прокси https:// Шифрованное соединение с прокси
SOCKS4 socks4:// Без аутентификации, только IPv4
SOCKS5 socks5:// С аутентификацией, UDP, IPv6
SOCKS5h socks5h:// DNS-резолвинг через прокси

Критически важно: если у вас SOCKS5-прокси, а вы указываете http:// — соединение не установится. Библиотека будет пытаться говорить по HTTP-протоколу с SOCKS-сервером.

Отсутствующие заголовки и fingerprint

Даже если прокси работает корректно, целевой сайт может блокировать запрос из-за подозрительных заголовков. Сравните:

Запрос из браузера

GET /api/data HTTP/1.1
Host: example.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36...
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1

Запрос из requests по умолчанию

GET /api/data HTTP/1.1
Host: example.com
User-Agent: python-requests/2.28.0
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive

Разница очевидна. Сайт с антибот-защитой мгновенно определит, что запрос идёт не из браузера.

Минимальный набор заголовков для маскировки

headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8",
    "Accept-Language": "en-US,en;q=0.9",
    "Accept-Encoding": "gzip, deflate, br",
    "Connection": "keep-alive",
    "Upgrade-Insecure-Requests": "1",
    "Sec-Fetch-Dest": "document",
    "Sec-Fetch-Mode": "navigate",
    "Sec-Fetch-Site": "none",
    "Sec-Fetch-User": "?1",
    "Cache-Control": "max-age=0"
}

SSL-сертификаты и верификация

Браузер имеет встроенное хранилище корневых сертификатов и умеет обрабатывать различные SSL-конфигурации. В коде могут возникать проблемы:

Ошибка SSL: CERTIFICATE_VERIFY_FAILED

Некоторые прокси используют собственные сертификаты для инспекции трафика. Браузер может иметь этот сертификат в доверенных, а ваш скрипт — нет.

# Временное решение для отладки (НЕ для продакшена!)
import requests
response = requests.get(url, proxies=proxies, verify=False)

# Правильное решение — указать путь к сертификату
response = requests.get(url, proxies=proxies, verify="/path/to/proxy-ca.crt")

Важно: Отключение верификации SSL (verify=False) делает соединение уязвимым для MITM-атак. Используйте только для отладки в безопасном окружении.

TLS Fingerprint

Продвинутые антибот-системы анализируют TLS fingerprint — порядок и набор шифров при установке соединения. Python requests использует стандартный набор, который отличается от браузерного.

Для обхода используйте библиотеки с кастомным TLS fingerprint:

# Установка: pip install curl-cffi
from curl_cffi import requests

response = requests.get(
    url,
    proxies={"https": proxy},
    impersonate="chrome120"  # Имитирует TLS fingerprint Chrome 120
)

DNS-утечки и резолвинг

Ещё одна неочевидная проблема — DNS-резолвинг. При использовании HTTP-прокси DNS-запрос может идти напрямую с вашей машины, минуя прокси.

Как это влияет на работу

  • Сайт видит реальный DNS-резолвер, а не прокси
  • Геолокация определяется неправильно
  • Некоторые сайты блокируют несовпадение IP и DNS-региона

Решение для SOCKS5

Используйте схему socks5h:// вместо socks5:// — буква "h" означает, что DNS-резолвинг будет выполняться на стороне прокси:

# DNS резолвится локально (утечка!)
proxy = "socks5://user:pass@proxy.example.com:1080"

# DNS резолвится через прокси (правильно)
proxy = "socks5h://user:pass@proxy.example.com:1080"

Рабочие примеры для Python, Node.js и cURL

Python с requests

import requests
from urllib.parse import quote

# Данные прокси
proxy_host = "proxy.example.com"
proxy_port = "8080"
proxy_user = "username"
proxy_pass = quote("p@ssword!", safe="")  # Экранируем спецсимволы

# Формируем URL прокси
proxy_url = f"http://{proxy_user}:{proxy_pass}@{proxy_host}:{proxy_port}"

proxies = {
    "http": proxy_url,
    "https": proxy_url
}

headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
    "Accept-Language": "en-US,en;q=0.9",
    "Accept-Encoding": "gzip, deflate, br",
}

try:
    response = requests.get(
        "https://httpbin.org/ip",
        proxies=proxies,
        headers=headers,
        timeout=30
    )
    print(f"Status: {response.status_code}")
    print(f"IP: {response.json()}")
except requests.exceptions.ProxyError as e:
    print(f"Ошибка прокси: {e}")
except requests.exceptions.ConnectTimeout:
    print("Таймаут подключения к прокси")

Python с aiohttp (асинхронный)

import aiohttp
import asyncio

async def fetch_with_proxy():
    proxy_url = "http://user:pass@proxy.example.com:8080"
    
    async with aiohttp.ClientSession() as session:
        async with session.get(
            "https://httpbin.org/ip",
            proxy=proxy_url,
            headers={"User-Agent": "Mozilla/5.0..."}
        ) as response:
            return await response.json()

result = asyncio.run(fetch_with_proxy())
print(result)

Node.js с axios

const axios = require('axios');
const HttpsProxyAgent = require('https-proxy-agent');

const proxyUrl = 'http://user:pass@proxy.example.com:8080';
const agent = new HttpsProxyAgent(proxyUrl);

axios.get('https://httpbin.org/ip', {
    httpsAgent: agent,
    headers: {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36...'
    }
})
.then(response => console.log(response.data))
.catch(error => console.error('Error:', error.message));

Node.js с node-fetch и SOCKS

const fetch = require('node-fetch');
const { SocksProxyAgent } = require('socks-proxy-agent');

const agent = new SocksProxyAgent('socks5://user:pass@proxy.example.com:1080');

fetch('https://httpbin.org/ip', { agent })
    .then(res => res.json())
    .then(data => console.log(data));

cURL

# HTTP прокси
curl -x "http://user:pass@proxy.example.com:8080" \
     -H "User-Agent: Mozilla/5.0..." \
     https://httpbin.org/ip

# SOCKS5 прокси с DNS через прокси
curl --socks5-hostname "proxy.example.com:1080" \
     --proxy-user "user:pass" \
     https://httpbin.org/ip

# Отладка — показать весь процесс подключения
curl -v -x "http://user:pass@proxy.example.com:8080" \
     https://httpbin.org/ip

Чек-лист диагностики

Если прокси не работает в коде, проверьте по порядку:

  1. Формат URL прокси — есть ли схема (http://, socks5://)?
  2. Спецсимволы в пароле — закодированы ли через URL-encoding?
  3. Тип прокси — совпадает ли указанный протокол с реальным?
  4. Аутентификация — по IP или по логину? IP сервера в whitelist?
  5. Заголовки — добавлен ли User-Agent и другие браузерные заголовки?
  6. SSL — нет ли ошибок сертификата?
  7. DNS — используется ли socks5h:// для резолвинга через прокси?
  8. Таймауты — достаточно ли времени на подключение (особенно для резидентных прокси)?

Заключение

Разница между браузером и кодом — в деталях: заголовки, протоколы, SSL, DNS. Браузер скрывает эту сложность, а в коде каждый аспект нужно настраивать явно. Начните с проверки формата URL и аутентификации, затем добавьте браузерные заголовки — это решает 90% проблем.

Для задач парсинга и автоматизации, где важна стабильность и низкий процент блокировок, хорошо подходят резидентные прокси — подробнее о них можно узнать на proxycove.com.