Guides · 7 min
Proxies in Selenium, Playwright and Puppeteer: Setup Guide
- TrueSocks editorial team
- 7 min read
All three frameworks route a browser through a proxy, but they differ in one important detail: authentication. Playwright accepts a username and password directly in its proxy settings, Puppeteer handles them with page.authenticate(), and Selenium with Chrome has no built-in way to pass proxy credentials, so you use an IP allowlist, a local forwarder or a helper extension. Below is working code for each, how to give every browser context its own IP, how to rotate a pool, and how to check that nothing leaks.
Key takeaways
5 points- Playwright: pass server, username and password in the proxy option at launch or per browser context.
- Puppeteer: start Chrome with --proxy-server and call page.authenticate() for the login and password.
- Selenium with Chrome ignores credentials in --proxy-server; use an IP allowlist or a local forwarder without auth.
- Chromium does not support SOCKS5 with a username and password; use the HTTP port or a forwarder for authenticated proxies.
- Give each context or profile its own IP and verify the exit IP and WebRTC before real work.
What you need before writing code
Every proxy comes as four values: host, port, login and password (or no login if your IP is allowlisted). Check them once before automating anything: paste the list into the free proxy checker to validate formats like ip:port:login:password, then run one request from the terminal.
curl -x http://login:[email protected]:8000 https://api.ipify.org| Framework | HTTP proxy | SOCKS5 proxy | Login and password |
|---|---|---|---|
| Playwright | Yes | Yes | Built in for HTTP proxies |
| Puppeteer | Yes, via Chrome flag | Yes, via Chrome flag | page.authenticate() for HTTP proxies |
| Selenium + Chrome | Yes, via Chrome flag | Yes, via Chrome flag | Not built in |
| Selenium + Firefox | Yes, via preferences | Yes, via preferences | Not built in, browser prompts for it |
If you are not sure whether to use HTTP or SOCKS5, read what is a SOCKS5 proxy. For browser automation, HTTP(S) is usually the simplest because every framework supports its authentication.
Playwright: proxy with authentication
Playwright has first-class proxy support. Set it at launch to apply it to the whole browser:
from playwright.sync_api import sync_playwright
PROXY = {
'server': 'http://203.0.113.10:8000',
'username': 'login',
'password': 'password',
}
with sync_playwright() as p:
browser = p.chromium.launch(proxy=PROXY)
page = browser.new_page()
page.goto('https://api.ipify.org?format=json')
print(page.text_content('body'))
browser.close()const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch({
proxy: { server: 'http://203.0.113.10:8000', username: 'login', password: 'password' },
});
const page = await browser.newPage();
await page.goto('https://api.ipify.org?format=json');
console.log(await page.textContent('body'));
await browser.close();
})();One IP per browser context
A browser context is an isolated session with its own cookies and storage. Playwright lets you set a different proxy for each context, so one browser process can run several identities side by side:
browser = p.chromium.launch()
ctx_a = browser.new_context(proxy={'server': 'http://203.0.113.10:8000', 'username': 'login', 'password': 'password'})
ctx_b = browser.new_context(proxy={'server': 'http://203.0.113.11:8000', 'username': 'login', 'password': 'password'})
for ctx in (ctx_a, ctx_b):
page = ctx.new_page()
page.goto('https://api.ipify.org')
print(page.text_content('body'))Puppeteer: --proxy-server plus page.authenticate()
Puppeteer passes the proxy to Chrome as a command-line flag. Credentials in the flag are ignored, so you supply them on the page with page.authenticate(), which answers the proxy's 407 challenge.
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch({
args: ['--proxy-server=http://203.0.113.10:8000'],
});
const page = await browser.newPage();
await page.authenticate({ username: 'login', password: 'password' });
await page.goto('https://api.ipify.org?format=json');
console.log(await page.evaluate(() => document.body.innerText));
await browser.close();
})();Call page.authenticate() on every new page. Recent Puppeteer versions can also set a proxy per browser context with browser.createBrowserContext({ proxyServer: 'http://203.0.113.11:8000' }), which is the Puppeteer equivalent of Playwright's per-context proxy.
Local forwarder for awkward cases
When an app cannot send credentials at all, run a local proxy without authentication that forwards to your real proxy with authentication. In Node.js the proxy-chain package does exactly this:
const proxyChain = require('proxy-chain');
const puppeteer = require('puppeteer');
(async () => {
const upstream = 'http://login:[email protected]:8000';
const local = await proxyChain.anonymizeProxy(upstream); // e.g. http://127.0.0.1:XXXXX
const browser = await puppeteer.launch({ args: ['--proxy-server=' + local] });
const page = await browser.newPage();
await page.goto('https://api.ipify.org');
console.log(await page.evaluate(() => document.body.innerText));
await browser.close();
await proxyChain.closeAnonymizedProxy(local, true);
})();Selenium: Chrome and Firefox
Chrome without authentication (IP allowlist)
If your proxy is authorized by your IP address, Selenium setup is one line:
from selenium import webdriver
from selenium.webdriver.common.by import By
options = webdriver.ChromeOptions()
options.add_argument('--proxy-server=http://203.0.113.10:8000')
driver = webdriver.Chrome(options=options)
driver.get('https://api.ipify.org?format=json')
print(driver.find_element(By.TAG_NAME, 'body').text)
driver.quit()Chrome with login and password
Chrome ignores login:password@ in --proxy-server and shows a login dialog that Selenium cannot fill. Practical options, from simplest to most flexible:
Switch to IP allowlisting
If the provider supports it, authorize the IP of the machine that runs the tests and drop the password entirely.
Run a local forwarder
Start a local proxy without auth that forwards to the real one with auth, for example with
gostor theproxy-chainpackage, and point--proxy-serverat127.0.0.1.Use a small extension
A helper extension can answer the proxy auth request with your credentials. It works, but you maintain it as Chrome's extension rules change.
Use the Firefox driver or Playwright
If you are free to choose, Playwright handles proxy auth natively and is often the shortest path.
gost -L http://127.0.0.1:8080 -F http://login:[email protected]:8000
# then in Selenium
options.add_argument('--proxy-server=http://127.0.0.1:8080')Firefox with SOCKS5 and remote DNS
from selenium import webdriver
options = webdriver.FirefoxOptions()
options.set_preference('network.proxy.type', 1)
options.set_preference('network.proxy.socks', '203.0.113.10')
options.set_preference('network.proxy.socks_port', 1080)
options.set_preference('network.proxy.socks_version', 5)
options.set_preference('network.proxy.socks_remote_dns', True)
driver = webdriver.Firefox(options=options)
driver.get('https://api.ipify.org')socks_remote_dns makes Firefox resolve hostnames on the proxy side, the same idea as socks5h:// in curl, so DNS lookups do not leak through your local resolver.
Rotating proxies across browser sessions
For automation, rotate per browser context or per browser instance, not per request. A browser session that changes IP mid-page looks broken to the site and loses challenge cookies. A simple pattern: a queue of proxies, one context per proxy, close the context when its unit of work is done.
import random
from playwright.sync_api import sync_playwright
POOL = ['203.0.113.10:8000', '203.0.113.11:8000', '203.0.113.12:8000']
URLS = ['https://example.com/a', 'https://example.com/b', 'https://example.com/c']
with sync_playwright() as p:
browser = p.chromium.launch()
for url in URLS:
hostport = random.choice(POOL)
ctx = browser.new_context(proxy={'server': 'http://' + hostport, 'username': 'login', 'password': 'password'})
page = ctx.new_page()
page.goto(url, timeout=30000)
print(url, page.title())
ctx.close()
browser.close()Whether to rotate at all depends on the task: logged-in sessions need a fixed IP, independent page loads can rotate. See rotating vs static proxies, and size the pool with how many proxies do I need.
Leaks and detection in automated browsers
- WebRTC. Chromium can reveal your real IP through WebRTC. The flag
--force-webrtc-ip-handling-policy=disable_non_proxied_udpkeeps WebRTC traffic on the proxy path. In Firefox, themedia.peerconnection.enabledpreference controls WebRTC. - DNS. With SOCKS5, resolve names on the proxy side (
socks5h, orsocks_remote_dnsin Firefox). - Timezone and locale. Playwright contexts accept
timezone_idandlocale; set them to match the proxy country. - Automation flags. Headless browsers expose signals such as
navigator.webdriver. A clean proxy does not hide them; strict sites check both.
ctx = browser.new_context(
proxy={'server': 'http://203.0.113.10:8000', 'username': 'login', 'password': 'password'},
locale='en-US',
timezone_id='America/New_York',
)Errors such as ERR_PROXY_CONNECTION_FAILED, ERR_TUNNEL_CONNECTION_FAILED or a 407 response are covered in proxy error 407 and other proxy errors.
Troubleshooting proxies in automated browsers
| Symptom | Likely cause | Fix |
|---|---|---|
| Login dialog appears and the test hangs | Proxy needs auth, but credentials were not passed | Use Playwright's username and password, page.authenticate() in Puppeteer, or a local forwarder for Selenium |
ERR_NO_SUPPORTED_PROXIES | SOCKS5 with credentials in Chromium | Use the HTTP port of the same proxy |
ERR_PROXY_CONNECTION_FAILED | Wrong host or port, or proxy unreachable | Test the same line with curl first |
ERR_TUNNEL_CONNECTION_FAILED | CONNECT refused, often a hidden 407 | Check credentials and the IP allowlist |
| Page shows your real country | Proxy not applied to this context, or WebRTC leak | Print the exit IP in the same context and set the WebRTC policy |
| Works locally, fails in CI | CI runner IP is not on the proxy allowlist | Allowlist the runner or switch to login and password |
Run each new proxy through one minimal script that only opens an IP echo page and prints the result. Keeping that script next to your project saves a lot of guessing later: if it passes and the real job fails, the problem is in the job, its timing or the target, not in the proxy setup.
Do you need a browser at all?
A real browser costs far more CPU, memory and proxy traffic than an HTTP client. If the data you need is in the initial HTML or in a JSON endpoint the page calls, a plain client such as Python requests with the same proxy is faster and cheaper. Reach for Playwright, Puppeteer or Selenium when the page builds its content in JavaScript, when the site runs browser challenges, or when you test your own front end. Many teams combine both: a browser to obtain cookies once, then a lightweight client for the bulk of requests through the same IP.
Which proxies to use for browser automation
- If you need
Testing your own sites and staging
PickDatacenter IPv4: fast, cheap and static, ideal for CI and geo tests.
View: Testing your own sites and staging - If you need
Scraping JavaScript-heavy public sites
PickRotating residential for protected targets, datacenter for easy ones.
View: Scraping JavaScript-heavy public sites - If you need
Automating your own accounts
PickStatic ISP proxies, one per account, never rotated mid-session.
View: Automating your own accounts - If you need
Checking mobile layouts and ads
PickMobile proxies, combined with mobile device emulation.
View: Checking mobile layouts and ads
All types are in the catalog with HTTP(S) and SOCKS5, delivered right after payment. For scraping strategy beyond the browser, see best proxies for web scraping and proxies for parsing.
Frequently asked questions
How do I use a proxy with authentication in Selenium?
Chrome ignores credentials in the --proxy-server flag. Use an IP allowlist, a local forwarder without auth that connects to the real proxy with auth, or a helper extension. Playwright supports proxy credentials natively.
Can Playwright use a different proxy for each browser context?
Yes. Pass a proxy option to browser.new_context() and each context will use its own IP, with its own cookies and storage.
Why does my SOCKS5 proxy with a password not work in Chrome automation?
Chromium does not support username and password authentication for SOCKS5 proxies. Use the HTTP port of the same proxy or a local forwarder.
How do I check which IP my automated browser uses?
Open an IP echo service such as api.ipify.org in the same page or context and print the result. Also check a WebRTC leak test once per setup.
Proxies for this task
Starting price per unit for the base term. Countries and volume discounts are listed on each product page.
- Choose country
Dedicated IPv4 proxies
68 countries, from $2.22 per IP / 30 days
- Choose country
Residential proxies
60 countries, from $1.85 per GB / 30 days
- Choose country
Static ISP proxies
60 countries, from $4.33 per IP / 30 days
- #selenium
- #playwright
- #puppeteer
- #browser automation
- #proxy setup
Proxies are provided for lawful use only. Spam, fraud and hacking are prohibited; follow the rules of the platforms you work with. Terms of service
Related articles
All articles- USEUse cases
Best Proxies for Web Scraping in 2026: How to Pick the Right Type
Which proxies work best for web scraping: datacenter, IPv6, ISP or residential. Match the proxy to the target, size your pool, choose rotation and avoid blocks.
8 min - GDGuides
Proxy Error 407 and Other Proxy Errors: Causes and Fixes
Fix 407 Proxy Authentication Required, 502, 503, 504, ERR_PROXY_CONNECTION_FAILED, ERR_TUNNEL_CONNECTION_FAILED and curl or Python proxy errors, step by step.
7 min - VSComparisons
Rotating vs Static Proxies: Which One Fits Your Task?
Rotating vs static proxies: how per-request rotation, sticky sessions and fixed IPs work, pricing, which tasks need each, and Python code to rotate your own proxy list.
7 min