How To Use a Proxy in Puppeteer: Setup, Rotation, and Authentication
Puppeteer is a Node.js library that controls headless Chromium for browser automation and web scraping. Without proxies, every request goes out from your real IP, and on protected sites, that IP gets blocked fast. This guide covers every method for configuring, authenticating, and rotating proxies in Puppeteer, plus how to troubleshoot the failures you'll actually run into. Not familiar with how proxies work? Check out what a proxy server is before continuing.
Vilius Sakutis
Last updated: Jun 15, 2026
18 min read

TL;DR
- Puppeteer automates headless Chromium for web scraping and browser testing, but without proxies, every request exposes your real IP, leading to blocks, CAPTCHAs, and geo-restrictions on protected sites
- This guide walks through setting up static and rotating proxies in Puppeteer using Node.js, covering 3 authentication methods (page.authenticate(), proxy-chain, and the Proxy-Authorization header), 3 rotation strategies, and advanced configurations including per-page proxies, proxy chaining, domain bypass, and bandwidth reduction.
- Chrome's --proxy-server flag silently ignores inline credentials; always authenticate using page.authenticate() or the proxy-chain package; install proxy-chain v2.x specifically, since v3.x dropped CommonJS support and breaks require()
- Before writing any Puppeteer code, verify your proxy credentials work independently with curl — if curl returns a 407 or connection error, the problem is your credentials or provider, not your script
- Prerequisites are Node.js v18+, npm, a Puppeteer installation (npm install puppeteer), and proxy credentials from your provider (hostname, port, username, password)
Prerequisites and environment setup
Step 1: Install Node.js and npm
You need Node.js v18 or higher. Download it from the official Node.js site and run the installer.
On Windows, check the box that adds Node to your PATH during installation. On macOS, you can use the installer or run:
Verify the install:
If you need a refresher on Node.js, check out our Node.js glossary entry.
Step 2: Create the project
Run the following commands one after the other to create your Node.js project and install Puppeteer:
During installation, Puppeteer downloads a bundled Chromium binary. The installation may take a while.
If you want to use an existing Chrome or Chromium installation instead, install puppeteer-core and pass executablePath to every puppeteer.launch() call:
Learn more about what headless browsers are and how they work: what is a headless browser? and headless Chrome.
Step 3: Verify the setup
Create a file named verify.js in your project folder and paste in the following code:
This confirms that Chromium launches correctly before you add any proxy logic. Run this command on the terminal to execute the file:
You should see verify.png appear in the project folder. On macOS, open it with open verify.png.
Step 4: Gather your proxy credentials
Before writing any proxy code, have these details ready from your Decodo’s dashboard:
- Hostname: gate.decodo.com
- Port: e.g., 10000
- Username and password: for Decodo, you’ll find these in your dashboard after purchasing a proxy plan
- Protocol: HTTP, HTTPS, or SOCKS5
How to set up a static proxy in Puppeteer
The --proxy-server flag is the foundation of proxy configuration in Puppeteer. Every method in this guide builds on it.
The code below sets up a static proxy in Puppeteer. Here, the --proxy-server flag is passed inside the args array of puppeteer.launch():
Since this script uses no authentication, it only works if you've whitelisted your IP in your Decodo dashboard. If you have, running the file (node static-proxy.js) returns your proxy's IP address instead of your real one:
For SOCKS5 proxies, swap the protocol prefix:
Read on SOCKS5 vs. HTTP proxies to understand when to use each, and explore HTTP proxies in more depth.
The inline credentials trap
One of the most common proxy configuration mistakes in Puppeteer is assuming that Chrome accepts credentials embedded in the proxy URL. While this format looks valid, Chrome silently ignores the username and password:
The tricky part is that Chrome doesn't throw an error when this happens. Instead, requests typically fail with a 407 Proxy Authentication Required response because the proxy never receives the authentication credentials.
The authentication methods covered in the next section provide the correct way to handle proxy authentication in Puppeteer.
How to verify that the proxy is working
Compare the IP returned by https://httpbin.org/ip with your actual public IP address. You can find your real IP by running the following command in your terminal.
Windows (Command Prompt):
macOS:
If the IP returned by Puppeteer matches your local IP, then the proxy is not being used. If the two IPs are different, the proxy is working correctly.
Free proxy lists won't cut it
Half are dead, the rest are flagged. Decodo's residential proxies give your Puppeteer scraper 115M+ clean IPs with automatic rotation. No list maintenance required.
How to authenticate proxies in Puppeteer
Chrome doesn’t support inline proxy credentials, so you need to use one of three alternative approaches. Below are the common methods and their tradeoffs.
Method 1: page.authenticate()
The page.authenticate() method is Puppeteer's built-in method for basic HTTP proxy authentication, and it must be called before page.goto() to work. Here’s the full code:
Limitation: page.authenticate() is scoped to a single page instance. Assuming you create a new page with browser.newPage(), you’ll need to call page.authenticate() again on that new page.
Method 2: Proxy-chain (recommended)
The proxy-chain package, developed by Apify, creates a local intermediary proxy server on 127.0.0.1 that handles authentication transparently. Chrome connects to the local address and never sees the real credentials.
For this tutorial, we’re installing v2.x, since v3.x dropped CommonJS support and will break require():
When it's working, your console output looks like this:
Remember to always call closeAnonymizedProxy() after closing the browser. Skipping it leaves the local proxy server running and causes resource leaks over time.
If your password contains special characters like @, :, or /, encode them before building the URL:
See Decodo’s authentication options to learn how we support both username/password authentication and IP whitelisting.
Method 3: Proxy-Authorization header
This approach uses page.setExtraHTTPHeaders() to send a Proxy-Authorization header containing Base64-encoded credentials. While it's worth knowing about, it has a major limitation: it only works with HTTP destinations and cannot authenticate proxy requests to HTTPS sites.
The reason is that HTTPS traffic is established through a CONNECT tunnel. Custom headers added with page.setExtraHTTPHeaders() are not forwarded during tunnel setup, so the proxy never receives the authentication credentials.
Use this approach only as a fallback for HTTP-only scenarios. For most use cases, page.authenticate() or proxy-chain are more reliable and work with both HTTP and HTTPS traffic.
Configuring rotating proxies in Puppeteer
IP rotation is one of the most effective ways to scale web scraping. Even residential IPs can be flagged or blocked if they generate too many requests from a single address. By rotating IPs, you can distribute traffic across multiple endpoints and reduce the risk of detection.
If you're new to the concept, see What Are Rotating Proxies?, IP Rotation, and Sticky vs. Rotating Sessions for a deeper understanding of how proxy rotation works and when to use different rotation strategies.
Below are three common approaches and the scenarios where each is most effective.
Random rotation from a proxy list
Select a proxy at random from a pool each time you launch a browser instance. This simple approach works well for small to medium-sized proxy pools and general-purpose scraping workloads where precise traffic distribution across IPs isn't a priority.
Note that when testing with a single Decodo gateway endpoint, it’s fine to reuse the same URL across all array entries, as we will still assign a different exit IP for each connection.
Sequential (round-robin) rotation
Iterate through the proxy list sequentially using an index counter so each proxy is used in order before any are reused. This is useful when you need a predictable distribution and guaranteed coverage across the entire pool.
Using a rotating proxy gateway endpoint
The simplest option for production scraping is a single rotating proxy endpoint. Providers automatically assign a new exit IP for each request, removing the need for any client-side rotation logic. With our rotating proxies, you simply pass one gateway URL, and each connection is routed through a fresh IP server-side.
How often you rotate depends on the target site and the size of your proxy pool – there’s no one-size-fits-all rule.
- Aggressive anti-bot sites (e.g., Google, Amazon, social platforms). rotate every request or every 2–3 requests.
- Moderately protected sites. Rotate every few minutes or per session.
- Lightly protected sites. Rotate every 10–20 requests.
In general, you should rotate more frequently when dealing with high data volumes, smaller proxy pools, or strict rate limits.
Advanced proxy configurations
Custom IP per page
Instead of assigning a single proxy per browser instance, you can route each page through a different proxy while running them in parallel. This is useful for simulating multiple users, testing geo-targeted content, or comparing regional pricing simultaneously.
The puppeteer-page-proxy package is often referenced for this use case. However, it is no longer compatible with recent Puppeteer versions and throws errors such as TypeError: useProxyPer[target.constructor.name] is not a function. A more reliable approach is to run separate browser instances and manage proxy assignment using proxy-chain.
Proxy chaining with proxy-chain
Proxy chaining routes your traffic through a local proxy server that forwards requests to one or more upstream proxies. This approach is useful when you want to add an extra layer of anonymity or route through multiple jurisdictions.
Bypassing the proxy for specific domains
Use --proxy-bypass-list to exclude certain domains from proxy routing. This is useful for local APIs, trusted CDNs, or internal services where routing traffic through a proxy adds unnecessary latency or complexity.
Reducing bandwidth through request interception
When scraping data-focused pages, assets such as images, fonts, and stylesheets add unnecessary bandwidth usage without improving the extracted data. Enabling page.setRequestInterception(true) and blocking these resources can significantly reduce proxy traffic and lower costs, especially on paid proxy plans.
Comparing Puppeteer’s approach with Playwright? See Puppeteer vs. Playwright and Playwright web scraping for a detailed breakdown of how they differ.
Choosing the right proxy type for Puppeteer
The proxy type you use often has a bigger impact on success rates than code-level optimizations. Matching the proxy to your target and workload is key to satisfactory performance.
Residential proxies
IPs sourced from real ISPs and home networks. These closely resemble genuine user traffic, making them the hardest for anti-bot systems to detect or block. To learn more, see Decodo residential proxies and what is a residential proxy?
- Best for: heavily protected sites (e.g., Google, Amazon, social platforms), geo-targeted scraping
- Trade-off: higher cost per GB and slightly higher latency than datacenter proxies
Datacenter proxies
IPs hosted in cloud or datacenter environments. They offer high-speed, large-scale availability at a lower cost.
- Best for: high-volume scraping on lightly protected sites, latency-sensitive tasks
- Trade-off: more easily detected and blocked by advanced anti-bot systems due to known IP ranges
ISP proxies
Proxies hosted on the datacenter infrastructure but registered with real ISP allocations. They combine the trust signals of residential IPs with the performance of datacenter networks.
- Best for: sites that block datacenter IPs but require lower latency than residential proxies
- Trade-off: sits in the middle ground on cost and scalability
SOCKS5 vs. HTTP
HTTP proxies operate at the application layer and are optimized for standard web traffic, making them sufficient for most scraping use cases.
SOCKS5 proxies operate at a lower level and support any protocol, not just HTTP/HTTPS. They are useful when you need broader protocol support or want an alternative routing method if HTTP proxies are being restricted.
Proxy type
Best for
Speed
Cost
Detection risk
Residential
Protected targets, geo-scraping
Moderate
Higher
Low
Datacenter
High-volume, low-protection targets
Fast
Lower
Higher
ISP
Mixed — trust + speed
Fast
Moderate
Low-moderate
SOCKS5
Non-HTTP traffic, protocol flexibility
Varies
Varies
Varies
Best practices
Test the proxy before writing any scraping logic
Run a curl command against your proxy endpoint first. On Windows Command Prompt or macOS Terminal:
If this returns an IP address, your credentials are valid. If it returns a 407, fix the credentials before touching Puppeteer.
Use headless: "new" when launching
Puppeteer's new headless mode has better stealth characteristics than the legacy mode:
Set a realistic user agent and viewport
A blank user agent or an unusual viewport size is a fingerprint in itself:
Add randomized delays between navigations
Human browsing isn't instant. A 2-8 second delay between requests significantly reduces detection risk:
Store credentials in environment variables
Never hardcode proxy credentials in source files. This is how you should do it:
Set them in your terminal before running.
Windows (Command Prompt):
macOS:
Common mistakes
Here are some common pitfalls to avoid:
- Passing credentials inline in --proxy-server. Chrome silently ignores user:pass@host:port in this flag. No error is thrown — requests just fail with 407. Always use page.authenticate() or proxy-chain.
- Installing proxy-chain v3.x. Version 3 dropped CommonJS support. require("proxy-chain") will throw ERR_REQUIRE_ESM. Install v2.x explicitly: npm install proxy-chain@2.
- Forgetting to call page.authenticate() before page.goto(). The order is to authenticate first, navigate second. Reversing the order means the first request goes out unauthenticated.
- Not closing anonymized proxies. Every anonymizeProxy() call opens a local server on a random port. Always call closeAnonymizedProxy() in a finally block to prevent port leaks.
- Using free public proxies for real work. Free proxies are slow, unreliable, and already banned on most protected sites. Use them only for smoke testing that the code runs, not for validating that the scraping logic works.
- Reusing the same IP too rapidly. Even with residential proxies, hitting a target 50 times per second from one IP will get that IP flagged. Rotate more frequently and add delays.
If you're dealing with anti-bot defenses, it's worth understanding the broader techniques used to avoid detection. For a deeper dive, see Navigating Anti-Bot Systems, Web Scraping Without Getting Blocked, and How to Bypass CAPTCHA with Puppeteer.
Troubleshooting proxy issues in Puppeteer
ERR_NO_SUPPORTED_PROXIES
Cause: The proxy string format is wrong, the protocol prefix doesn't match what the provider expects, or credentials aren't being passed through correctly.
Fix: Confirm the format with curl first. If curl works but Puppeteer doesn't, you're likely missing the proxy-chain wrapping; Chrome can't handle inline credentials.
ERR_INVALID_AUTH_CREDENTIALS
Cause: The proxy received the request but rejected the credentials. It could be a wrong username, a wrong password, or the proxy plan isn't active.
Fix: Re-copy credentials fresh from your provider dashboard. Watch for leading/trailing spaces and special characters. Test with curl independently before going back to the Puppeteer code.
407 Proxy Authentication Required
Cause: Either no credentials were provided, or the wrong authentication method was used.
Fix: Switch from the inline URL format to page.authenticate() or proxy-chain. Double-check that page.authenticate() is called before page.goto().
503 Service Temporarily Unavailable
Cause. The specific exit node the proxy assigned is temporarily overloaded. This is a provider-side issue, not a code problem.
Fix: Run the script again. Rotating proxy endpoints assign a different exit node on each connection, so a second attempt will almost always succeed.
Pages are loading slowly or timing out
Cause: The proxy endpoint is geographically distant or overloaded. Default Puppeteer timeouts (30 seconds) may not be enough.
Fix: Increase the timeout and test proxy latency independently:
CAPTCHA walls are appearing despite proxies
Cause: The proxy IP has been flagged, or the browser fingerprint is identifiable as automated traffic.
Fix: Rotate to a fresh IP, set a realistic user agent and viewport, and add delays. If you're using datacenter proxies, switch to residential — they're significantly harder to fingerprint. See how to bypass CAPTCHAs.
Implementing retry logic with exponential backoff
Wrap your scraping logic in a retry loop. On failure, switch to a new proxy and wait progressively longer between attempts:
See retry logic for more on the pattern.
Debugging tips
1. Launch in headful mode to see exactly what Chromium is doing:
2. Listen to request failures to catch proxy-related issues at the network level:
3. Test without a proxy first. If the issue reproduces without a proxy, it's your scraping logic, not the proxy. Isolating this saves a lot of debugging time.
Full code reference
All scripts used in this guide are ready to copy and run. Replace YOUR_USERNAME and YOUR_PASSWORD with your Decodo credentials throughout.
verify.js
static-proxy.js
auth-page.js
auth-proxychain.js
auth-header.js
rotate-random.js
rotate-roundrobin.js
rotate-gateway.js
advanced-per-page.js
advanced-chaining.js
advanced-bypass.js
advanced-block-resources.js
troubleshoot-retry.js
Final thoughts
This guide covered the full spectrum of proxy configuration in Puppeteer, from the basic --proxy-server setup to authentication, rotation strategies, advanced per-page routing, and production-ready retry handling.
A few things worth keeping in mind as you ship:
- proxy-chain v2.x is the most reliable authentication method, so use it as your default
- Always verify credentials with curl before writing any Puppeteer code
- Gateway rotation endpoints handle IP cycling server-side and are the cleanest setup for production scraping
- Residential proxies are the right choice for protected targets; the extra cost is worth it when the alternative is a blocked IP
Our residential proxies are compatible with all the methods covered in this guide out of the box. The rotating endpoint supports both username/password authentication and IP whitelisting, geo-targeting enables routing to specific countries or cities, and automatic rotation handles IP cycling without requiring any client-side logic.
Proxy sorted, anti-bot not?
Decodo's Web Scraping API handles proxy rotation, CAPTCHA solving, and fingerprint evasion so your Puppeteer scripts don't need stealth plugins and workarounds.
About the author

Vilius Sakutis
Head of Partnerships
Vilius leads performance marketing initiatives with expertize rooted in affiliates and SaaS marketing strategies. Armed with a Master's in International Marketing and Management, he combines academic insight with hands-on experience to drive measurable results in digital marketing campaigns.
Connect with Vilius via LinkedIn
All information on Decodo Blog is provided on an as is basis and for informational purposes only. We make no representation and disclaim all liability with respect to your use of any information contained on Decodo Blog or any third-party websites that may belinked therein.


