GUIDE · PROXY SETUP
How to use a proxy with Puppeteer
Puppeteer drives Chrome, and Chrome takes its proxy from a launch argument. The login goes in a separate call. This guide shows both parts, a script that prints the IP address the website sees, and what to check when a page will not load.
Last updated October 31, 2026
How the two pieces fit
Puppeteer launches a real Chrome or Chromium browser. You point that browser at a proxy by passing the --proxy-server argument in the launch args. That argument carries only the scheme, host and port. The username and password go in page.authenticate(), which Puppeteer's documentation describes as providing credentials for HTTP authentication. Its note says request interception is turned on behind the scenes to implement authentication, which might affect performance.
Any proxy provider works the same way. You need a host, a port and a login, or an IP authorization set up with the provider so no login is needed. The placeholders are PROXY_HOST, PROXY_PORT, PROXY_USER and PROXY_PASS. Only use proxies for lawful purposes and within the terms of the websites you visit. A proxy is not a cloak: the proxy provider and the sites you visit can still see activity, so do not treat a proxy as making your traffic anonymous.
What Webshare's documentation says about connecting
We use Webshare as the example because it is a common starting point and has a free plan. We have not tested it. The details below come from Webshare's help center article on the rotating proxy endpoint and its API documentation on proxy connections, as read on October 31, 2026. Your own dashboard shows your real values, so copy them from there and not from this page.
Rotating endpoint: the help center says the domain name is p.webshare.io, the default port is 80, and the alternative ports are 1080, 3128, or any port between 9999 and 29999. It describes this method as rotating proxies randomly within your list for each request. Direct connections are different: the API docs say they use the proxy address and port fields from the Proxy List API, so each proxy in your list has its own address and port.
Authentication: the API docs describe two ways. With username and password authentication you provide the username and password along with the proxy address and port. With IP authorization only the address and port are needed, with no credentials, because Webshare recognizes the IP address you connect from.
Username options: for backbone connections the API docs say you can add parameters to your username with hyphens, for example a two-letter country code, a numeric session ID for a sticky session, or the word rotate for a new IP on every request. We have not checked which of these your plan includes, so check your dashboard before relying on them.
A complete example script
This script launches Chrome through a proxy, signs in, loads a page that shows your IP address and prints it. Credentials come from environment variables, so set PROXY_USER and PROXY_PASS in your terminal before running it. Replace PROXY_HOST and PROXY_PORT with the values from your dashboard.
If you do not have a proxy yet, Webshare has a free plan you can try (Affiliate link) and shows your host, port and login in its dashboard. Check its current plan limits and terms on its own site first, as they can change.
const puppeteer = require("puppeteer");
(async () => {
const browser = await puppeteer.launch({
headless: true,
args: ["--proxy-server=http://PROXY_HOST:PROXY_PORT"],
});
const page = await browser.newPage();
// Must be called before navigating to a page that asks for a login.
await page.authenticate({
username: process.env.PROXY_USER,
password: process.env.PROXY_PASS,
});
await page.goto("https://api.ipify.org?format=json", {
waitUntil: "domcontentloaded",
timeout: 30000,
});
console.log(await page.evaluate(() => document.body.innerText));
await browser.close();
})();Call page.authenticate before navigating. If you navigate first, the proxy asks for a login that has not been supplied and the page fails. Because authenticate is a method on a page, call it on each page you open.
Using IP authorization instead
If your provider supports IP authorization, you can skip page.authenticate entirely. Webshare's API documentation describes this as needing only the proxy address and port, with no credentials. It is simpler, but it only works from an IP address you have authorized, so it will stop working from a laptop on a different network.
Rotating endpoints and sessions
With a rotating endpoint, each connection can come out of a different address. Webshare's help center describes its endpoint as p.webshare.io with a default port of 80 and rotation within your list for each request, and its API docs describe username parameters for country, session and rotate. Whether you see a new address within one page load depends on how connections are reused, which we have not tested. If you need one address for a whole browsing session, use a sticky session option from your provider and check your dashboard for the exact username format.
Handle failures
Wrap navigation in try and finally so the browser always closes, even when the proxy fails. A leftover Chrome process can use a lot of memory.
const puppeteer = require("puppeteer");
(async () => {
const browser = await puppeteer.launch({
args: ["--proxy-server=http://PROXY_HOST:PROXY_PORT"],
});
try {
const page = await browser.newPage();
await page.authenticate({
username: process.env.PROXY_USER,
password: process.env.PROXY_PASS,
});
await page.goto("https://example.com", { timeout: 30000 });
console.log(await page.title());
} catch (err) {
console.error("Navigation failed:", err.message);
} finally {
await browser.close();
}
})();Common messages and what they usually mean: ERR_PROXY_CONNECTION_FAILED means Chrome could not reach the proxy, so check host, port and your firewall. ERR_TUNNEL_CONNECTION_FAILED can mean a wrong port or a proxy that is down. A 407 or a repeating login prompt means the credentials were not supplied in time or are wrong.
Check that it worked
Compare your public IP address with and without the proxy. Open our what is my IP tool in a normal browser tab and note the address, then run the same check through the proxy. The two addresses should differ if traffic really goes through the proxy. You can also run your proxy through our proxy checker, or look up the address you see with the IP lookup tool to see its country and network.
If the address did not change, the proxy is probably not being used. If you get an error instead, see the troubleshooting section.
Good habits
- Keep credentials in environment variables, not in the script.
- Add delays between page loads and keep concurrency modest. Hammering a site is rude and is a quick way to get an address blocked.
- Respect robots.txt and each site's terms of service.
- Close the browser in a finally block.
Related reading
If you do not need a full browser, plain HTTP requests are lighter: see Python requests or Scrapy. To compare proxy types, see residential vs datacenter proxies.
Once your proxy works, the next step is choosing a plan that fits your use. See Webshare's current plans (Affiliate link) or compare other providers; the setup above stays the same either way.
Frequently asked questions
How do I set a proxy in Puppeteer?
Pass --proxy-server=http://host:port in the args array of puppeteer.launch(). If the proxy needs a login, call page.authenticate with a username and password before navigating.
Can I put the username and password in the --proxy-server argument?
Chrome does not take credentials from that argument. Use page.authenticate, or IP authorization if your provider offers it.
Does page.authenticate slow things down?
Puppeteer's documentation says request interception is turned on behind the scenes to implement authentication, which might affect performance.
Can I use a different proxy for each page?
The launch argument applies to the whole browser. For different proxies you can launch separate browser instances, each with its own argument.