Puppeteer Screenshot with Proxy Settings: Route Browser Traffic Through a Proxy
Route Puppeteer page loads through an HTTP, HTTPS, or SOCKS5 proxy, then capture a screenshot with reliable waits and clear troubleshooting.
To route a Puppeteer screenshot through a proxy, pass Chromium’s --proxy-server switch in puppeteer.launch({ args }), navigate to the page, wait for the state your target needs, and call page.screenshot(). Replace the example proxy hostname and port with an endpoint supplied by your proxy operator.
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch({
args: ['--proxy-server=http://proxy.example:8080'],
});
try {
const page = await browser.newPage();
await page.goto('https://example.com', { waitUntil: 'networkidle2' });
await page.screenshot({ path: 'capture.png', fullPage: true });
} finally {
await browser.close();
}
proxy.example:8080 is a placeholder, not a proxy service or tested endpoint. Puppeteer passes the launch argument to Chromium; page.screenshot() captures the page but does not configure its network route. See the Puppeteer launch options, Chromium network settings, and Puppeteer screenshot guide.
1. Set up a Puppeteer project
Use a recent Node.js release supported by the Puppeteer version you install. In a new project, install Puppeteer:
npm install puppeteer
The puppeteer package downloads a compatible browser for its standard setup. If you use puppeteer-core, provide an installed browser through executablePath or channel when launching it. The example uses ECMAScript modules; add "type": "module" to package.json or save it with an .mjs extension.
2. Route page requests through an HTTP proxy
Chromium accepts a proxy URI in --proxy-server. A single proxy URI applies to URL loads across schemes:
import puppeteer from 'puppeteer';
const targetUrl = 'https://example.com';
const proxyUrl = 'http://proxy.example:8080'; // Replace with your provider's endpoint.
const outputPath = 'capture.png';
const browser = await puppeteer.launch({
args: [`--proxy-server=${proxyUrl}`],
});
try {
const page = await browser.newPage();
await page.goto(targetUrl, { waitUntil: 'networkidle2', timeout: 45_000 });
await page.screenshot({ path: outputPath, fullPage: true });
console.log(`Saved ${outputPath}`);
} finally {
await browser.close();
}
The navigation timeout is an example value, not a guarantee that a particular site will load within that time. Adjust it to your environment and workload. The browser closes in finally, including when navigation or capture throws, so a failed page does not leave that browser process open.
Choose a readiness condition
waitUntil: 'networkidle2' is a useful starting point when the page settles after network activity, but it is not universally correct. Analytics, streaming connections, long polling, and other ongoing requests can prevent an idle condition; a page can also reach network idle before client-side content you need appears. Pick a readiness signal that matches the page:
domcontentloadedwaits for the initial document to be parsed. Use it when your next step waits explicitly for a relevant element.loadwaits for the load event and its dependent resources. It may wait longer than needed on pages with slow resources.networkidle2waits for a period with no more than two active network connections. It can be useful for pages that settle, but may not fit sites with persistent requests.- After navigation, use
page.waitForSelector('main')or a more specific selector when a known element indicates that the content is ready.
For a selector-based wait, keep the navigation and element wait separate:
await page.goto(targetUrl, { waitUntil: 'domcontentloaded', timeout: 45_000 });
await page.waitForSelector('main article', { timeout: 15_000 });
await page.screenshot({ path: 'article.png', fullPage: true });
3. Capture the viewport, full page, or a region
The proxy setting does not change screenshot options. By default, Puppeteer captures the current viewport. Set fullPage: true to capture the full page, including content beyond the viewport. Use clip for a rectangular region, or an element screenshot when only one DOM element is needed. See the ScreenshotOptions reference.
// Full page
await page.screenshot({ path: 'full.png', fullPage: true });
// A viewport-sized capture (the default)
await page.screenshot({ path: 'viewport.png' });
// A rectangular clip in page coordinates
await page.screenshot({
path: 'region.png',
clip: { x: 80, y: 120, width: 900, height: 500 },
});
// Capture one element
const card = await page.waitForSelector('.product-card');
await card.screenshot({ path: 'card.png' });
Coordinates and dimensions for a clip must describe a valid rectangle within the rendered page. If the capture is unexpectedly empty or clipped, check the element bounds and page dimensions. For long pages, full-page capture can consume more memory and time than a viewport screenshot.
4. Select the right proxy protocol and routing behavior
Use the scheme and endpoint supplied by your proxy operator. These forms are not interchangeable labels:
| Need | Example launch argument | Notes |
|---|---|---|
| HTTP proxy | --proxy-server=http://proxy.example:8080 |
Use when the endpoint is an HTTP proxy. |
| Secure web proxy transport | --proxy-server=https://proxy.example:8443 |
Use HTTPS when the proxy itself supports secure HTTPS transport. |
| SOCKS5 proxy | --proxy-server=socks5://proxy.example:1080 |
Use the SOCKS5 scheme for a SOCKS5 endpoint. |
Chromium supports a single proxy URI and scheme-specific mappings. A mapping can route HTTP and HTTPS destinations to different proxies:
const browser = await puppeteer.launch({
args: [
'--proxy-server=http=http-proxy.example:8080;https=https-proxy.example:8443',
],
});
Use scheme-specific routing only when your network setup requires it; confirm the syntax and endpoints with your proxy provider. Chromium also supports a proxy auto-configuration URL with --proxy-pac-url when your environment supplies a PAC file.
Bypass selected hosts
A bypass rule makes matching destinations connect directly rather than through the configured proxy. Chromium’s --proxy-bypass-list takes effect alongside --proxy-server:
const browser = await puppeteer.launch({
args: [
'--proxy-server=http://proxy.example:8080',
'--proxy-bypass-list=localhost;127.0.0.1;internal.example',
],
});
Only add hosts that should bypass the proxy. If the requirement is that a destination must use the proxy, do not include it in the bypass list. Check Chromium’s network settings documentation for proxy and bypass behavior.
SOCKS5 and DNS behavior
Chromium’s SOCKS guidance warns that the proxy switch applies to URL loads, while some browser components can still resolve names directly. Therefore, configuring SOCKS5 does not by itself guarantee that every DNS request from the browser uses the proxy. If your network requirements demand remote hostname resolution through SOCKS, review Chromium’s host resolver rules and DNS caveats for your Chrome version and environment. Do not assume that a page-load proxy setting covers every browser subsystem.
5. Handle proxy authentication carefully
Proxy authentication varies by proxy type, provider, and Chromium behavior. The sources for this guide do not establish one universal Puppeteer recipe for credentials, so do not assume that placing a username and password in the proxy URL will work across setups. Check your provider’s current instructions for the exact endpoint and authentication method, then verify it with the Chrome version you run.
Keep credentials out of source control and logs. Load secrets from your runtime environment or secret store, and avoid printing a full proxy URL if it contains credentials. If your provider requires a particular authentication flow, follow its documentation rather than changing the scheme or guessing at Chromium flags.
6. Run the capture with Python or cURL
Puppeteer is a Node.js browser automation library; the proxy argument belongs to Chromium’s launch configuration. Python and cURL examples below show how to send HTTP requests through a proxy, but they do not launch Puppeteer or render a browser screenshot. Use them when the task is an HTTP request or when you want to check proxy connectivity separately.
Python: make an HTTP request through a proxy
import requests
proxy = 'http://proxy.example:8080' # Replace with your proxy endpoint.
proxies = {'http': proxy, 'https': proxy}
response = requests.get(
'https://example.com',
proxies=proxies,
timeout=30,
)
response.raise_for_status()
print(response.status_code)
print(response.url)
cURL: make an HTTP request through a proxy
curl --proxy http://proxy.example:8080 --max-time 30 https://example.com
These checks can help distinguish a proxy connectivity problem from a Puppeteer navigation or screenshot problem. A successful plain HTTP request does not prove that a browser’s authentication, DNS, or page-rendering behavior is configured correctly.
7. Troubleshoot common failures
| Symptom | Likely cause | What to check |
|---|---|---|
| Navigation times out | The endpoint is unreachable, the proxy cannot reach the target, or the chosen readiness condition never occurs. | Confirm the proxy URI and port with its operator. Try a less restrictive navigation condition, then wait for the specific content selector you need. Set a deliberate timeout and handle the resulting error. |
| Proxy connection or tunnel error | Wrong scheme, host, port, or proxy transport for the endpoint. | Use the protocol the provider specifies: HTTP, HTTPS transport to the proxy, or SOCKS5. Do not relabel one proxy type as another. |
| Authentication prompt or rejected request | Credentials or authentication method do not match the proxy’s requirements. | Follow the provider’s current Chrome/Puppeteer instructions. There is no universal credential-in-URL recipe established here. |
| Page loads directly instead of using the proxy | A bypass rule matches the destination, or the argument is missing or malformed. | Inspect the exact launch args, remove unintended bypass entries, and verify the configured proxy against the provider endpoint. |
| Some hostnames resolve outside SOCKS | Chromium may perform DNS activity outside the URL-load proxy path. | Review Chromium’s SOCKS DNS guidance and host resolver rules for the required privacy and network behavior. |
| Screenshot is blank or content is missing | Capture ran before the page’s content was ready, or the target returned an interstitial, bot check, or error page. | Wait for a meaningful selector, inspect the page title and URL, and capture only after the intended content appears. |
| Full-page capture is unexpectedly large or slow | The page is long, has many assets, or keeps loading content as it scrolls. | Use a viewport or clipped/element capture if that matches the requirement. Wait for lazy content deliberately and account for the extra browser memory and time. |
puppeteer-core cannot find a browser |
No browser executable or channel was configured. | Set an appropriate executablePath or channel in launch options. |
8. Performance, reliability, and cost considerations
- Browser lifecycle: Reusing a browser can avoid repeated startup cost in a capture service, but isolate pages and close them after use. Always close the browser or return it to a controlled pool, even after exceptions.
- Readiness: Waiting for full network idle can add latency or hang on persistent connections. Waiting for a specific element can be more aligned with the content you need, but depends on a stable selector.
- Capture size: Full-page screenshots and high-resolution pages use more memory and take longer than viewport captures. Clip or capture one element when a full page is unnecessary.
- Proxy dependency: The proxy adds a network dependency and can introduce connection failures or extra latency. Retries may help transient failures, but use bounded retries and avoid retrying permanent authentication or configuration errors indefinitely.
- Operational cost: Your total cost depends on your browser compute, proxy provider, bandwidth, and capture volume. The research sources do not establish proxy prices or performance benchmarks; estimate using your own provider and workload.
9. Or skip the browser setup
ScreenshotNeo is a website screenshot API and MCP server. One GET request returns a PNG, JPEG, WebP, or PDF. For configuration and options, see the ScreenshotNeo API documentation.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
timeout=90,
)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
- Cookie and consent banners, newsletter popups, and chat widgets are removed before the capture; each cleanup step can be turned off.
- Bot checks and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are never billed; response headers identify the page verdict and billing status.
- An MCP server provides
take_screenshot,get_page_info, andcapture_pdftools for Claude, Cursor, and other MCP clients. - The free plan includes 1,000 screenshots a month with no card. Paid plans start at $5 for 3,000 screenshots; yearly billing gives two months free, and every feature is on every plan.
Sign up for 1,000 free screenshots a month, with no card required.
10. FAQ
Does the proxy setting change the screenshot API?
No. --proxy-server configures Chromium’s routing at browser launch. The screenshot call captures the rendered page.
Can I use a different proxy for HTTP and HTTPS destinations?
Chromium supports scheme-specific proxy mappings. Use them only when the proxy endpoints and routing requirements call for separate routes.
Does fullPage: true scroll the page like a user?
It requests a full-page image. Pages that load content only after scrolling may need an explicit scroll-and-wait routine before capture.
Will SOCKS5 proxy every DNS lookup?
Not necessarily. Chromium warns that some DNS activity can occur outside the proxy path used for URL loads. Review the SOCKS guidance and host resolver rules for your environment.
Can I take a screenshot without running Chromium?
Yes. ScreenshotNeo accepts a URL through its API and returns an image or PDF, so you can use its HTTP endpoint instead of managing a Puppeteer browser for that capture.


