Почему прокси работает в браузере, но не в коде: полный разбор проблемы
Классическая ситуация: настроили прокси в браузере, открыли сайт — всё работает. Запустили скрипт с тем же прокси — ошибка подключения, таймаут или бан. Разбираемся, почему так происходит и как это исправить.
Чем запрос из браузера отличается от запроса из кода
Когда вы открываете сайт в браузере через прокси, происходит гораздо больше, чем просто 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
Чек-лист диагностики
Если прокси не работает в коде, проверьте по порядку:
- Формат URL прокси — есть ли схема (http://, socks5://)?
- Спецсимволы в пароле — закодированы ли через URL-encoding?
- Тип прокси — совпадает ли указанный протокол с реальным?
- Аутентификация — по IP или по логину? IP сервера в whitelist?
- Заголовки — добавлен ли User-Agent и другие браузерные заголовки?
- SSL — нет ли ошибок сертификата?
- DNS — используется ли socks5h:// для резолвинга через прокси?
- Таймауты — достаточно ли времени на подключение (особенно для резидентных прокси)?
Заключение
Разница между браузером и кодом — в деталях: заголовки, протоколы, SSL, DNS. Браузер скрывает эту сложность, а в коде каждый аспект нужно настраивать явно. Начните с проверки формата URL и аутентификации, затем добавьте браузерные заголовки — это решает 90% проблем.
Для задач парсинга и автоматизации, где важна стабильность и низкий процент блокировок, хорошо подходят резидентные прокси — подробнее о них можно узнать на proxycove.com.