Вот ловушка, в которую ровно один раз попадает каждый разработчик браузерной автоматизации: вы добавляете --proxy-server=http://user:pass@host:port в флаги запуска Chrome, браузер нормально стартует, а потом каждая страница виснет на всплывающем окне аутентификации, которое ваш скрипт не видит и не может нажать. Chrome молча вырезает учётные данные из флага прокси. Ни ошибки, ни строки в логе — просто модальное окно в браузере, на который никто не смотрит.
Этот гайд — полная карта методов аутентификации прокси, которые реально работают в Selenium и Playwright в 2026 году: какой использовать, какого избегать и точный код для каждого. (О выборе прокси и стратегии ротации в этих инструментах см. Использование прокси с Playwright и Puppeteer — эта статья исключительно о том, как протолкнуть учётные данные.)
HTTP-клиенты командной строки сами вытаскивают учётные данные из URL прокси и отвечают на 407 Proxy Authentication Required заголовком Proxy-Authorization автоматически. Chromium — нет: --proxy-server принимает только scheme://host:port, а когда возвращается 407, он показывает интерактивный диалог. В headless-режиме диалога нет вовсе — запрос просто падает.
Так что вопрос никогда не звучит «как засунуть пароль в URL» — он звучит «какой слой ответит на 407 за меня». Четыре рабочих ответа, лучший — первым.
Playwright решил проблему как надо: он сам отвечает на челленджи прокси-аутентификации через CDP. Учётные данные передаются в опциях запуска или контекста:
# Python
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch(
proxy={
"server": "http://us.jibaoproxy.com:913",
"username": "USERNAME-session-job1",
"password": "PASSWORD",
}
)
// Node.js - и на уровне контекста, где это и становится мощным:
const browser = await chromium.launch({
proxy: { server: 'per-context' } // объявляем намерение при запуске
});
const ctx = await browser.newContext({
proxy: {
server: 'http://us.jibaoproxy.com:913',
username: 'USERNAME-session-job2',
password: 'PASSWORD',
}
});
Прокси на уровне контекста означают, что один браузерный процесс может запускать N контекстов на N разных sticky-сессиях — фундамент любой серьёзной схемы скрапинга на Playwright. Если вы на Playwright, вы закончили; переходите к разделу про проверку. Всё, что ниже, существует только потому, что у Selenium нет эквивалента.
selenium-wire оборачивает Selenium локальным MITM-прокси, который обрабатывает аутентификацию вышестоящего прокси:
from seleniumwire import webdriver # pip install selenium-wire
options = {
"proxy": {
"http": "http://USERNAME:[email protected]:913",
"https": "https://USERNAME:[email protected]:913",
}
}
driver = webdriver.Chrome(seleniumwire_options=options)
driver.get("https://example.com")
Что стоит знать до продакшена:
Классический подход: крошечное сгенерированное расширение, которое задаёт прокси и отвечает на аутентификацию через chrome.webRequest.onAuthRequired. Без MITM, без лишнего процесса, работает в чистом Selenium:
import zipfile, json
HOST, PORT = "us.jibaoproxy.com", 913
USER, PASS = "USERNAME-session-sel1", "PASSWORD"
manifest = {
"version": "1.0.0", "manifest_version": 2, "name": "Proxy Auth",
"permissions": ["proxy", "webRequest", "webRequestBlocking", ""],
"background": {"scripts": ["background.js"]},
}
background = f"""
chrome.proxy.settings.set({{value: {{mode: "fixed_servers", rules: {{
singleProxy: {{scheme: "http", host: "{HOST}", port: {PORT}}}
}}}}, scope: "regular"}}, () => {{}});
chrome.webRequest.onAuthRequired.addListener(
() => ({{authCredentials: {{username: "{USER}", password: "{PASS}"}}}}),
{{urls: [""]}}, ["blocking"]
);
"""
with zipfile.ZipFile("proxy_auth.zip", "w") as zp:
zp.writestr("manifest.json", json.dumps(manifest))
zp.writestr("background.js", background)
from selenium import webdriver
opts = webdriver.ChromeOptions()
opts.add_extension("proxy_auth.zip")
driver = webdriver.Chrome(options=opts)
Две тонкости: расширения не грузятся в классическом headless-режиме — используйте --headless=new (Chrome 109+) или запускайте с головой; и фоновые скрипты Manifest V2 всё ещё работают для сайдлоад-автоматизации, но следите за таймлайном депрекации MV2 в Chrome — эквиваленту на MV3 нужен service worker и тот же слушатель onAuthRequired.
Запустите крошечный локальный прокси, который держит учётные данные; направьте браузер на localhost вообще без аутентификации. Работает с любым браузером, любым драйвером, любым языком, в headless или нет:
# Через pproxy (pip install pproxy) - терминал 1:
pproxy -l http://127.0.0.1:8899 \
-r "http://USERNAME:[email protected]:913"
# Ваш скрипт - терминал 2: чистый Selenium, аутентификация не нужна
opts = webdriver.ChromeOptions()
opts.add_argument("--proxy-server=http://127.0.0.1:8899")
driver = webdriver.Chrome(options=opts)
Это же самый чистый ответ для Firefox/geckodriver (где трюк с расширением не применим) и для экзотики вроде тестирования Electron-приложений. Цена: ещё одна движущаяся деталь под присмотром, а маршрутизация на уровне контекста требует по одному порту форвардера на сессию.
| Метод | Работает в | Headless | Сессии на контекст | Вердикт для продакшена |
|---|---|---|---|---|
| Нативный Playwright | Playwright | Да | Да | Выбор по умолчанию |
| selenium-wire | Selenium (Py) | Да | На драйвер | Норм при умеренном масштабе |
| Auth-расширение | Selenium (любой язык) | Только --headless=new | На драйвер | Надёжно, следите за MV3 |
| Локальный форвардер | Всё | Да | Один порт на сессию | Универсальный запасной |
Молчаливый откат на ваш настоящий IP — это тот режим отказа, из-за которого банят аккаунты. Проверяйте исходящий IP в начале каждого запуска:
ip = driver.execute_script(
"return fetch('https://api.ipify.org').then(r => r.text())"
) if hasattr(driver, 'execute_script') else page.evaluate(
"fetch('https://api.ipify.org').then(r => r.text())"
)
assert ip != MY_REAL_IP, "Proxy not applied - aborting before we leak"
--proxy-server; Chrome их выбросил. Используйте один из четырёх методов.ERR_PROXY_CONNECTION_FAILED — неверный хост/порт или несовпадение протокола. Сначала проверьте ту же строку через curl -x; если curl работает, проблема в обвязке браузера.--headless=new.Браузеры не читают прокси-URL вида user:pass@ — кто-то должен ответить на 407 за них. В Playwright это встроено; в Selenium это selenium-wire, сгенерированное auth-расширение или локальный форвардер. Выбирайте по своему стеку из таблицы сравнения, а затем проверяйте исходящий IP при каждом запуске, чтобы молчаливый сбой аутентификации никогда не утёк ваш настоящий адрес посреди краулинга.
$5 бесплатного резидентного баланса — один шлюз, sticky или ротация, HTTP и SOCKS5.
Начать бесплатноНовым пользователям — 5U при регистрации, бонус к первому пополнению. Акция ограничена по времени.