Set up Puppeteer with ProxyField
- Check the login once with curl (section 1).
- Launch Chrome with
args: ['--proxy-server=http://gate.proxyfield.io:7000'], with no credentials in the flag. - Call
page.authenticate({ username, password })on each new page before its firstgoto(). - Open
https://api.ipify.org?format=jsonto see the exit IP.
The same login in a Puppeteer script (an ES module), with a New York exit IP.
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch({ args: ['--proxy-server=http://gate.proxyfield.io:7000'] });
const page = await browser.newPage();
await page.authenticate({ username: '<login>-country-us-city-new_york', password: '<password>' });
await page.goto('https://api.ipify.org');
console.log(await page.evaluate(() => document.body.innerText));
await browser.close();Puppeteer's own reference for the login call: Page.authenticate() method (Puppeteer docs).
Mobile 5G/4G proxies cost $3.45/GB for 1 GB down to $2.45/GB from 1,000 GB on the same gateway and balance, over HTTP, HTTPS and SOCKS5.
The examples read your ProxyField username and password from two environment variables, PROXY_USER and PROXY_PASS, and print the exit IP from https://api.ipify.org. Puppeteer 25 needs Node.js 22.12 or later.
On this page 12 sections
1. Check the credentials with cURL
Run one request through the gateway before you open Puppeteer. If this fails, the problem is the credentials or your network, not your script.
curl -x gate.proxyfield.io:7000 -U "<login>-country-us-city-new_york:<password>" https://api.ipify.orgReplace <login> and <password> with your ProxyField username and password. You should see an IP that is not yours. Check its country with https://ipinfo.io/json, or test your proxy with the ProxyField proxy checker. More flags are in the cURL proxy guide.
2. Puppeteer proxy auth example: --proxy-server + page.authenticate()
Pass the gateway to Chrome with --proxy-server, then give the page your username and password with page.authenticate():
// npm i puppeteer
const puppeteer = require('puppeteer');
const { PROXY_USER, PROXY_PASS } = process.env; // your ProxyField username and password
(async () => {
const browser = await puppeteer.launch({
args: ['--proxy-server=http://gate.proxyfield.io:7000'],
});
const page = await browser.newPage();
await page.authenticate({
username: `${PROXY_USER}-country-us`,
password: PROXY_PASS,
});
await page.goto('https://api.ipify.org?format=json');
console.log(await page.evaluate(() => document.body.innerText));
await browser.close();
})();Save it as proxy.js and run it with your credentials:
PROXY_USER=<login> PROXY_PASS=<password> node proxy.jsIt prints {"ip":"..."} with a US IP. Three rules keep it working:
- No credentials in the flag. Chrome rejects a username and password inside
--proxy-server:http://<login>:<password>@gate.proxyfield.io:7000fails withnet::ERR_NO_SUPPORTED_PROXIES. That is whypage.authenticateexists. - Authenticate every page. Call
page.authenticate()on each new page before its firstgoto(). A page without it fails withnet::ERR_INVALID_AUTH_CREDENTIALS. - Targeting lives in the username.
<login>-country-usexits in the United States; section 6 lists the other parameters.
To set a proxy for your everyday browser instead of a script, see Chrome proxy settings.
Which npm packages a Puppeteer proxy needs
Only puppeteer for an HTTP proxy with a username and password: --proxy-server and page.authenticate() are built in. Two more packages help in special cases.
| Package | Install | When you need it |
|---|---|---|
puppeteer | npm i puppeteer | Always; it downloads a matching Chrome |
proxy-chain | npm i proxy-chain | SOCKS5 with a username and password, or one set of credentials for every page (section 5) |
puppeteer-extra | npm i puppeteer-extra | Plugins such as stealth (section 8) |
For Node.js requests without a browser, such as fetch, see the Node.js proxy setup .
3. One proxy per browser context
browser.createBrowserContext({ proxyServer }) gives each browser context its own proxy, so one browser can exit in several countries at once. The method is createBrowserContext() in Puppeteer 22 and later; older versions called it createIncognitoBrowserContext().
const puppeteer = require('puppeteer');
const { PROXY_USER, PROXY_PASS } = process.env;
async function checkIp(browser, country) {
const context = await browser.createBrowserContext({
proxyServer: 'http://gate.proxyfield.io:7000',
});
const page = await context.newPage();
await page.authenticate({
username: `${PROXY_USER}-country-${country}`,
password: PROXY_PASS,
});
await page.goto('https://api.ipify.org?format=json');
console.log(country, await page.evaluate(() => document.body.innerText));
await context.close();
}
(async () => {
const browser = await puppeteer.launch();
await Promise.all([checkIp(browser, 'us'), checkIp(browser, 'gb')]);
await browser.close();
})();gb is the United Kingdom (<login>-country-gb).
Countries take their two-letter code in lowercase, as in <login>-country-gb.
Each context has its own cookies, cache and connections, and page.authenticate() still runs once per page. The script prints two lines, one IP per country.
4. Rotate proxies so each page gets a different IP
With ProxyField you don't keep a proxy list: the gateway gives a new IP on every request, and a sticky session holds one IP from 30 s up to 36 h. In Puppeteer, the connection decides which IP you get.
Read moreShow less
Method A, a new page per URL, does not rotate reliably. Chrome keeps its connection to the proxy open and can reuse it for the next page in the same context, so on per-request rotation the next page can keep the same IP.
Method B, a new browser context per URL. A new context opens new connections, so the gateway can assign a new IP to each URL:
const puppeteer = require('puppeteer');
const { PROXY_USER, PROXY_PASS } = process.env;
const urls = [
'https://api.ipify.org?format=json',
'https://api.ipify.org?format=json',
'https://api.ipify.org?format=json',
];
(async () => {
const browser = await puppeteer.launch();
for (const url of urls) {
// A new context opens new connections, so the gateway can assign a new IP.
const context = await browser.createBrowserContext({
proxyServer: 'http://gate.proxyfield.io:7000',
});
const page = await context.newPage();
await page.authenticate({ username: `${PROXY_USER}-country-us`, password: PROXY_PASS });
await page.goto(url);
console.log(await page.evaluate(() => document.body.innerText));
await context.close();
}
await browser.close();
})();Two things to know:
- One page load opens several connections (the HTML, then scripts and images from other hosts). On per-request rotation, each of them can leave from a different IP. If a site ties a login or a cart to one IP, use a sticky session.
- A sticky session takes a session parameter in the username:
<login>-country-usplus-session-<id>-ttl-<seconds>after the location, as in<login>-country-us-session-he9f2qga-ttl-600(ttlis 30 to 129,600 seconds, 20 minutes by default). It holds the IP from 30 s to 36 h, with presets of 1, 5, 10 and 30 min and 1, 3 and 6 h.
5. SOCKS5 in Puppeteer
A socks5:// address in --proxy-server works, but only without credentials: Chrome can't send a SOCKS5 username and password, and page.authenticate() doesn't apply to SOCKS5. You have two options.
Option 1: allowlist your IP. Add the IP of the machine that runs Puppeteer to the allowlist in your dashboard, then connect without a username to the port you set up in the dashboard (<port> below):
const puppeteer = require('puppeteer');
(async () => {
// Works when your IP is on the allowlist: Chrome sends no SOCKS5 credentials.
// <port> is the port you set up in the dashboard for your allowlisted IP.
const browser = await puppeteer.launch({
args: ['--proxy-server=socks5://gate.proxyfield.io:<port>'],
});
const page = await browser.newPage();
await page.goto('https://api.ipify.org?format=json');
console.log(await page.evaluate(() => document.body.innerText));
await browser.close();
})();Read moreShow less
With an allowlisted IP there is no username to carry the country: with the allowlist, the product, location and session are set per port in the dashboard.
Option 2: run a local forwarder. The proxy-chain package starts a local proxy without authentication that forwards to the gateway with your credentials:
// npm i puppeteer proxy-chain
const puppeteer = require('puppeteer');
const proxyChain = require('proxy-chain');
const { PROXY_USER, PROXY_PASS } = process.env;
(async () => {
// A local proxy without auth that forwards to the gateway with your credentials.
const upstream = `socks5://${encodeURIComponent(PROXY_USER)}:${encodeURIComponent(PROXY_PASS)}@gate.proxyfield.io:7000`;
const localProxy = await proxyChain.anonymizeProxy(upstream);
const browser = await puppeteer.launch({ args: [`--proxy-server=${localProxy}`] });
const page = await browser.newPage();
await page.goto('https://api.ipify.org?format=json');
console.log(await page.evaluate(() => document.body.innerText));
await browser.close();
await proxyChain.closeAnonymizedProxy(localProxy, true);
})();encodeURIComponent keeps special characters in the password from breaking the URL (p@ss becomes p%40ss). proxy-chain accepts a socks5:// upstream with a username and password. SOCKS5 takes the same username and password. Replace socks5:// with http:// and the same forwarder sends your credentials over HTTP instead. See SOCKS5 proxies for when SOCKS5 is worth it.
6. Country, city and sticky sessions
Targeting goes in the username. ProxyField targets by country, region (state) and city, and covers all 50 US states.
| Goal | Username |
|---|---|
| Any US IP, a new IP on every request | <login>-country-us |
| An IP in New York City | <login>-country-us-city-new_ |
| An IP in the United Kingdom | <login>-country-gb |
| An IP in one US state | <login>-country-us-region-new_ |
| One IP for 30 s to 36 h (sticky session) | <login>-country-us-session-he9f2qga-ttl-600 |
| Mobile 4G/5G instead of residential | residential by default; add -mobile after the login for mobile IPs, as in <login>-mobile-country-us |
ASN, ZIP and carrier targeting are not offered. See the product pages for residential proxies and mobile 4G/5G proxies.
7. Cut traffic: block images, fonts and media
Billing is per GB, so every image, font and video a page loads costs traffic. If your script needs only the HTML or the text, block the rest:
const puppeteer = require('puppeteer');
const { PROXY_USER, PROXY_PASS } = process.env;
const BLOCKED = ['image', 'font', 'media'];
(async () => {
const browser = await puppeteer.launch({
args: ['--proxy-server=http://gate.proxyfield.io:7000'],
});
const page = await browser.newPage();
await page.authenticate({ username: `${PROXY_USER}-country-us`, password: PROXY_PASS });
await page.setRequestInterception(true);
page.on('request', (request) => {
if (BLOCKED.includes(request.resourceType())) request.abort();
else request.continue();
});
await page.goto('https://example.com');
console.log(await page.title());
await browser.close();
})();Request interception works alongside page.authenticate(). The dashboard shows your usage in real time and the gigabytes you have left, so you can compare a run with and without blocking.
8. User agent, headers and stealth
Set the user agent with page.setUserAgent() and other headers with page.setExtraHTTPHeaders(), before page.goto():
const puppeteer = require('puppeteer');
const { PROXY_USER, PROXY_PASS } = process.env;
(async () => {
const browser = await puppeteer.launch({
args: ['--proxy-server=http://gate.proxyfield.io:7000'],
});
const page = await browser.newPage();
await page.authenticate({ username: `${PROXY_USER}-country-us`, password: PROXY_PASS });
await page.setUserAgent({
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/155.0.0.0 Safari/537.36',
});
await page.setExtraHTTPHeaders({ 'Accept-Language': 'en-US,en;q=0.9' });
await page.goto('https://httpbin.org/headers');
console.log(await page.evaluate(() => document.body.innerText));
await browser.close();
})();Read moreShow less
In Puppeteer 25, page.setUserAgent() takes an options object; the older string form still works but is deprecated. Keep Accept-Language in line with the proxy country.
Sites can read headless Chrome's automation signals, such as navigator.webdriver; a proxy changes the IP, its location and its reputation, not those signals (our browser fingerprinting guide ).
puppeteer-extra with a proxy: stealth and user preferences plugins
puppeteer-extra wraps Puppeteer and adds plugins with puppeteer.use(). It launches Chrome the same way, so the proxy setup doesn't change: --proxy-server at launch, page.authenticate() on each page.
// npm i puppeteer puppeteer-extra puppeteer-extra-plugin-stealth
const puppeteer = require('puppeteer-extra');
const StealthPlugin = require('puppeteer-extra-plugin-stealth');
puppeteer.use(StealthPlugin());
const { PROXY_USER, PROXY_PASS } = process.env;
(async () => {
const browser = await puppeteer.launch({
args: ['--proxy-server=http://gate.proxyfield.io:7000'],
});
const page = await browser.newPage();
await page.authenticate({ username: `${PROXY_USER}-country-us`, password: PROXY_PASS });
await page.goto('https://api.ipify.org?format=json');
console.log(await page.evaluate(() => document.body.innerText));
await browser.close();
})();puppeteer-extra-plugin-user-preferences writes Chrome profile preferences, such as the default font size, from a userPrefs object before launch. It sets browser preferences only; the proxy still goes in --proxy-server. If a bundled build (webpack, esbuild, a serverless package) fails with Cannot find module 'puppeteer-extra-plugin-user-preferences', install it as a direct dependency: puppeteer-extra loads plugins by name at runtime, and bundlers miss those imports. puppeteer-extra and its plugins were last released in 2023, so test them with your Puppeteer version.
9. Fix ERR_TUNNEL_CONNECTION_FAILED and other proxy errors
net::ERR_TUNNEL_CONNECTION_FAILED means Chrome reached the proxy, but the proxy answered the tunnel (CONNECT) request with an error instead of connecting to the site. Check the scheme, the credentials, whether the site is up, and whether it falls in a restricted category.
Read moreShow less
| Symptom | Likely cause | Fix |
|---|---|---|
net:: | The proxy could not open the tunnel to the site | Check the scheme (http:// or socks5://), the credentials and whether the site is up. Some site categories are restricted to protect the network, including financial and government services (our Acceptable Use Policy). |
| The page loads with status 407 (Proxy Authentication Required) | Wrong username or password, or your IP is not on the allowlist | Copy the credentials from the dashboard again and check the username parameters. |
net:: | page.authenticate() was not called on this page, or was called after goto() | Call it on every new page before its first goto(). |
net:: | Credentials inside --proxy-server (http://<login>:<password>@... or socks5://<login>:<password>@...) | Remove them; use page.authenticate, the allowlist or a local forwarder. |
net:: or a timeout | A wrong host or port, or a firewall that blocks port 7000, or the port you set up in the dashboard for the allowlist | Use gate.proxyfield.io:7000 with your username and password (with the allowlist, the port you set up in the dashboard) and run the cURL check from section 1. |
| The IP is in the wrong country | A typo in the username, or a connection reused from an earlier page | Check the spelling and open a new browser context. |
| The site loads without the proxy | The host is on --proxy-bypass-list or proxyBypassList, or it is localhost, which Chrome doesn't send through a proxy by default | Remove the host from the bypass list and test with a public URL. |
The gateway's own status codes: 407 Proxy Authentication Required for a wrong login or password, an IP not on the allowlist or a malformed location, 402 Payment Required when no traffic is left and 502 Bad Gateway when no exit could be connected; a restricted site gets no separate response, and a 403 comes from the site itself. The full list is in proxy errors.
10. Python
Puppeteer is a Node.js library; in Python, the Playwright proxy setup takes the same gateway (more in our Puppeteer guide ).
Get started
Start with the Easy Trial: 1 GB for $2.95, no minimum purchase, no subscription, and the traffic never expires. All purchases are final. See pricing per GB.
The same --proxy-server setup works in headless and headed mode; other headless tools are covered in our proxy setup hub and the alternatives to Puppeteer in our Puppeteer alternatives guide .
Puppeteer Proxy Setup FAQ
How do I authenticate a residential proxy in Puppeteer?
ProxyField residential proxies authenticate in Puppeteer with one call, from $1.95/GB: launch with --proxy-server=http://gate.proxyfield.io:7000, then call page.authenticate({ username, password }) on each page before goto(). Chrome rejects credentials written into the flag, so page.authenticate() is how you pass them.
How do I rotate proxies in Puppeteer so each page gets a different IP?
Open a new browser context per URL with createBrowserContext({ proxyServer }): a new context opens new connections, so the gateway assigns a new IP. Pages in one context can reuse an open connection and keep the same IP (section 4).
Why does Puppeteer fail with ERR_NO_SUPPORTED_PROXIES?
The --proxy-server flag holds a username and password, such as http://<login>:<password>@gate.proxyfield.io:7000, and Chrome rejects them there. Remove them and use page.authenticate, the IP allowlist or a local forwarder.
Why does Puppeteer return ERR_TUNNEL_CONNECTION_FAILED?
The proxy could not open the tunnel to the site: a wrong scheme, failed authentication, an unreachable site or a restricted site category. The checklist is in section 9 of this page.
Does Puppeteer support SOCKS5 proxies?
Yes, through a socks5:// address in --proxy-server, but without credentials. Use an IP allowlist with the port you set up in the dashboard, or a local forwarder such as proxy-chain that adds the username and password and connects to gate.proxyfield.io:7000 (section 5).
Does Puppeteer have built-in proxy support?
Puppeteer has no proxy of its own; it uses the proxy you give Chrome with --proxy-server, or one per browser context with createBrowserContext({ proxyServer }). page.authenticate() is built in for the username and password.
How do I use a different proxy per browser context in Puppeteer?
Pass proxyServer to browser.createBrowserContext() (Puppeteer 22 and later), then call page.authenticate() on each page in it. Each context can carry its own country in the username, so one browser can exit in several countries at once.
Can Puppeteer change the proxy without restarting the browser?
Yes. Open a new browser context with its own proxyServer, or authenticate a new page with another username, such as <login>-country-gb instead of <login>-country-us. The launch-level --proxy-server itself only changes with a new browser.
What type of proxy works best with Puppeteer?
Residential proxies for most public-data scraping, mobile 5G/4G proxies for targets that treat carrier IPs differently, and rotating datacenter proxies at $0.29/GB down to $0.22/GB for sites that accept datacenter IPs.
Do I need proxies with Puppeteer?
Not for a few pages, but a scraper that sends many requests from one IP soon meets rate limits, and some pages differ by country. A rotating gateway spreads the requests over many IPs and lets you pick the location.
Show 9 more questionsShow fewer questions
Does routing Puppeteer through a residential proxy slow down page loads?
A little: every request takes an extra hop through the gateway and the exit IP. Typical residential responses are under 1 s, according to our service data, and blocking images, fonts and media cuts both time and traffic (section 7).
Does headless browser scraping use more proxy traffic?
Yes: a full page load pulls the HTML plus scripts, styles, images, fonts and media, and all of it counts toward your GB. Block what your script doesn't need with request interception; the dashboard shows real-time usage to compare runs.
Can I use ProxyField proxies with Puppeteer or Playwright?
Yes, both. The same gateway and credentials work; only the place of the username differs: page.authenticate() in Puppeteer, the proxy option in Playwright.
Do I have to rewrite my Puppeteer code to use ProxyField?
No. Add the --proxy-server argument to puppeteer.launch() and one page.authenticate() call per page; the rest of your script stays the same.
What is puppeteer-extra, and does it work with a proxy?
puppeteer-extra is a wrapper around Puppeteer that adds plugins, such as stealth and user preferences, with puppeteer.use(). It launches Chrome the same way, so the proxy works unchanged; its last release dates from 2023.
What is puppeteer-extra-plugin-stealth?
A puppeteer-extra plugin that hides some of the automation signals headless Chrome exposes, such as navigator.webdriver. It was last released in 2023 and detection has moved on since, so test it with your targets; the proxy setup doesn't change.
Can proxies solve CAPTCHAs in Puppeteer?
No. A proxy changes the IP, its location and its reputation, and spreading requests over many IPs cuts rate-limit blocks. It does not solve CAPTCHAs or hide a headless browser's fingerprint or TLS signature, which Cloudflare, HUMAN (formerly PerimeterX) and DataDome also check.
How do I use a proxy with Pyppeteer?
The same way as in Puppeteer: args=['--proxy-server=http://gate.proxyfield.io:7000'] in launch() and await page.authenticate({...}) per page. Its PyPI page says the project is unmaintained (last release 2.0.0, February 2024), so Playwright for Python is the safer choice.
Are there free proxies for Puppeteer?
Public free proxy lists exist, but their IPs are shared, often slow or already blocked, and rarely take a username and password. ProxyField does not offer free proxies; the smallest purchase is 1 GB for $2.95.