ScreenshotNeo

BlogHow-to

Screenshot a Website with a Proxy Server Using Puppeteer

Route Puppeteer page traffic through a proxy, then save a full-page or element screenshot. Includes setup, authentication notes, options, and troubleshooting.

By the ScreenshotNeo team4 October 20268 min read

To screenshot a website through a proxy with Puppeteer, pass Chromium a proxy server setting when launching the browser, navigate to the target page, and call page.screenshot(). The example below uses Chromium’s --proxy-server launch argument, which routes browser page requests through the supplied endpoint. Use a proxy URL and credentials supplied by your provider; do not commit real credentials.

Puppeteer documents browser launch arguments through LaunchOptions.args, and its screenshot guide uses Page.screenshot() after navigation. See the LaunchOptions API, Puppeteer screenshot guide, and ScreenshotOptions reference.

1. Install Puppeteer

Use a current Node.js release supported by the Puppeteer version you install. In a new project, run:

npm init -y
npm install puppeteer

The puppeteer package downloads a compatible Chrome for Testing browser during installation. If you use puppeteer-core, provide a compatible browser binary yourself.

2. Set proxy details as environment variables

Set PROXY_SERVER to the endpoint provided by your proxy service. The value should include the scheme and port, for example http://proxy.example.net:8080 or socks5://proxy.example.net:1080, if that protocol is supported by your provider and browser. These are illustrative formats, not working proxy services.

export PROXY_SERVER='http://proxy.example.net:8080'
# Set these only when your proxy provider requires authentication.
export PROXY_USERNAME='your-proxy-username'
export PROXY_PASSWORD='your-proxy-password'

In PowerShell, use $env:PROXY_SERVER='http://proxy.example.net:8080' and set the other values the same way. Keep secrets in your deployment secret store in production. Avoid putting credentials in source control, screenshots, logs, or process arguments.

3. Launch Puppeteer through the proxy and capture the page

Save this as screenshot.mjs. It accepts the target URL as its first command-line argument, waits for the page’s load event, and saves a full-page PNG. The timeout and network-idle wait are bounded so a page with persistent analytics or streaming requests does not hang indefinitely.

import puppeteer from 'puppeteer';

const target = process.argv[2] ?? 'https://example.com';
const proxyServer = process.env.PROXY_SERVER;
const proxyUsername = process.env.PROXY_USERNAME;
const proxyPassword = process.env.PROXY_PASSWORD;

if (!proxyServer) {
  throw new Error('Set PROXY_SERVER to your proxy endpoint, including scheme and port.');
}

const browser = await puppeteer.launch({
  headless: true,
  args: [`--proxy-server=${proxyServer}`],
});

try {
  const page = await browser.newPage();
  await page.setViewport({ width: 1440, height: 900, deviceScaleFactor: 1 });

  // This method handles HTTP authentication challenges. Whether it works for
  // your proxy depends on its protocol and authentication scheme.
  if (proxyUsername || proxyPassword) {
    if (!proxyUsername || !proxyPassword) {
      throw new Error('Set both PROXY_USERNAME and PROXY_PASSWORD, or neither.');
    }
    await page.authenticate({ username: proxyUsername, password: proxyPassword });
  }

  const response = await page.goto(target, {
    waitUntil: 'load',
    timeout: 60000,
  });

  // Optional: allow delayed content to settle. This may time out on pages with
  // long-polling, analytics, or other connections that never become idle.
  try {
    await page.waitForNetworkIdle({ idleTime: 500, timeout: 10000 });
  } catch {
    console.warn('Network did not become idle before the optional wait timed out.');
  }

  console.log(`Navigated to ${page.url()} (HTTP ${response?.status() ?? 'unknown'})`);
  await page.screenshot({ path: 'website.png', fullPage: true, type: 'png' });
  console.log('Saved website.png');
} finally {
  await browser.close();
}

Run it with:

node screenshot.mjs https://example.com

The Chromium proxy argument is passed at browser launch. Use a separate browser process when you need separate proxy settings; all pages in one launched browser share its process-level proxy configuration. Puppeteer’s page.authenticate() is documented for HTTP authentication and enables request interception behind the scenes, which can affect performance. It does not establish compatibility with every proxy protocol or provider’s authentication method. Check your provider’s instructions if authentication fails.

4. Choose the right capture settings

Need Option What to know
Entire rendered document fullPage: true Captures beyond the current viewport. Very tall pages can use substantial memory and produce large files.
Viewport only Omit fullPage or set it to false Captures the current viewport dimensions.
Specific rectangle clip: { x, y, width, height } Coordinates are page screenshot coordinates; use a valid positive region within the rendered page.
One DOM element elementHandle.screenshot() Wait for the selector, then call screenshot on its handle. Puppeteer scrolls the element into view when needed.
JPEG or WebP type: 'jpeg' or type: 'webp' Set quality from 0 to 100 where supported. Quality does not apply to PNG.
Transparent background omitBackground: true Useful for transparent PNG output; page backgrounds and content can still paint their own colors.

For a component screenshot, replace the screenshot call with this pattern:

const card = await page.waitForSelector('.product-card', { timeout: 15000 });
if (!card) throw new Error('Could not find .product-card');
await card.screenshot({ path: 'product-card.png', type: 'png' });

For a compressed JPEG viewport capture, use await page.screenshot({ path: 'website.jpg', type: 'jpeg', quality: 82 }). The screenshot file extension can determine the image type if type is omitted.

5. Handle proxy routing details and edge cases

  • HTTP and HTTPS destinations: The proxy must support the traffic and tunneling behavior needed for the destination protocol. Browser proxy support and authentication details vary by proxy type and provider.
  • Proxy bypass: Chromium supports bypass configuration as part of its proxy setup; consult the Chromium and provider documentation for the exact bypass syntax your deployment requires. Do not assume Puppeteer’s @puppeteer/browsers environment-variable support configures page traffic. Those documented variables apply to that package’s browser-management library and CLI when proxy-agent is installed.
  • Authentication: Keep usernames and passwords out of URLs where possible. page.authenticate() handles HTTP authentication challenges, but provider-specific schemes may require a proxy extension, a provider gateway, or another supported authentication method.
  • Redirects and final URL: A successful navigation can redirect. Check page.url() and the returned response status rather than assuming the requested address is the final page.
  • Consent dialogs, bot checks, and JavaScript-rendered content: A proxy changes the route and possibly the apparent network location; it does not guarantee that a page will render, that a challenge will be passed, or that overlays will disappear.
  • Local and private targets: A proxy can change which network can reach a destination. Only capture sites and networks you are authorized to access, and take care not to route sensitive internal URLs through an untrusted proxy.
  • Large pages: Full-page capture can be expensive for very long pages. Capture a specific element or viewport if that is all you need.

6. Troubleshoot common failures

Symptom Likely cause Fix
Browser starts but navigation fails with a proxy error Wrong endpoint, unavailable proxy, unsupported scheme, or network access blocked from the runtime. Check the host, port, scheme, firewall, and provider’s protocol instructions. Test connectivity from the same machine or container.
Proxy returns an authentication error Missing or incorrect credentials, or an authentication scheme unsupported by the page.authenticate() flow. Verify credentials and provider requirements. Do not infer that HTTP authentication support covers every proxy-auth mechanism.
Navigation times out Slow proxy, slow site, blocked subresource, or a page that never reaches the selected wait condition. Increase the navigation timeout if appropriate, use waitUntil: 'domcontentloaded' for earlier capture, then wait explicitly for the element or content you need.
Screenshot is blank or missing content Capture ran before client-side rendering completed, target content is lazy-loaded, or the site served an error/challenge page. Wait for a meaningful selector with page.waitForSelector(); inspect page.url(), response status, and page text before saving.
Page loads, but traffic appears to bypass the proxy Proxy configured on a different browser process, proxy arguments malformed, or environment variables were mistaken for browser page routing. Pass the proxy argument to the launch that owns the page. Confirm the actual egress address using an endpoint you control or your provider’s diagnostic service.
Some resources fail while the document loads The site or proxy blocks third-party hosts, images, fonts, or scripts. Inspect failed requests and provider rules. Decide whether those resources are necessary for the screenshot rather than treating document navigation as proof every asset loaded.
Screenshot call errors or output is huge Invalid clip bounds, unusually tall page, or memory pressure from full-page capture. Check dimensions, capture a smaller region or element, or use a viewport screenshot.
Process remains open after an error Browser cleanup was skipped on an exception. Use try/finally and close the browser in the finally block, as in the runnable script.

7. Performance, reliability, and cost

Every screenshot requires launching or reusing a browser, loading the page through the proxy, waiting for the needed content, rasterizing it, and writing the image. Proxy latency adds to navigation time, and proxy outages can turn otherwise healthy captures into failures. For repeated captures, reuse a browser process where operationally appropriate, while isolating jobs that need different proxy settings or browser state. Apply a bounded timeout, close pages and browsers reliably, and avoid waiting for network idle when the site maintains open connections.

Full-page images and high device scale factors increase memory use and output size. Use PNG for lossless output, or JPEG/WebP quality controls when smaller lossy files fit the use case. The cost of this do-it-yourself approach depends on your compute, proxy provider, traffic volume, and operational overhead; the research sources provide no common benchmark or universal price. Factor in browser hosting, proxy charges, retries, and maintenance.

Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server for developers. It can return a PNG, JPEG, WebP, or PDF from one GET request, with no Puppeteer process for you to operate. Its cookie handling accepts the consent banner like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each step can be turned off. Bot checks and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and the response identifies the page verdict and billing status in headers. Its MCP server gives AI agents tools for screenshots, page information, and PDF capture. See the ScreenshotNeo website and 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}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await Bun.write('shot.webp', res);

Cookie banners, popups, and chat widgets are removed before the shot; bot checks, blank pages, and failed loads are never billed; an MCP server lets AI agents take screenshots. The free plan includes 1,000 screenshots a month with no card, and paid plans start at $5 for 3,000. Sign up for ScreenshotNeo and get 1,000 free screenshots a month, with no card.

FAQ

Does Puppeteer itself provide a proxy service?

No. Puppeteer controls the browser; you supply and operate the proxy endpoint.

Can I use a different proxy for each page in one browser?

The launch argument configures the browser process, so use separate browser processes for distinct proxy routes. Puppeteer’s next-version BrowserContext options document per-context proxy settings, but verify support in the exact Puppeteer release you deploy before relying on it.

Will a proxy make every site accessible?

No. Access depends on the destination, the proxy’s network and rules, the site’s response, and any authentication or bot checks.

Can I capture PDFs instead of images?

Yes. Puppeteer provides page.pdf() for PDF output; configure paper size and print behavior separately from screenshot options.