ScreenshotNeo

BlogHow-to

How to Use Custom Proxies for Website Screenshots

Route Playwright screenshot traffic through HTTP or SOCKS proxies, scope settings correctly, debug failures, and compare a managed ScreenshotNeo option.

By the ScreenshotNeo team1 October 20269 min read

Direct answer: In Playwright, pass a proxy object to chromium.launch() to route every browser context through one proxy, or pass it to browser.newContext() when only one context should use it. The object can include a server URI, optional username and password, and a comma-separated bypass list. Then navigate normally and call page.screenshot().

import { chromium } from 'playwright';

const browser = await chromium.launch({
  proxy: {
    server: process.env.PROXY_SERVER, // e.g. http://proxy.example:3128 or socks5://proxy.example:1080
    username: process.env.PROXY_USER,
    password: process.env.PROXY_PASSWORD,
    bypass: 'localhost,127.0.0.1',
  },
});

const context = await browser.newContext();
const page = await context.newPage();
await page.goto('https://example.com', { waitUntil: 'networkidle' });
await page.screenshot({ path: 'screenshot.png', fullPage: true });
await browser.close();

Playwright documents HTTP(S) and SOCKSv5 proxy support, browser-level and context-level configuration, credentials, and bypass hosts in its proxy guide and browser API reference. A proxy can change where traffic exits, but it does not grant permission to capture a site or guarantee that a site’s controls will be bypassed. Follow the target site’s rules and your organization’s policy.

1. Get the proxy details

Ask your proxy administrator or provider for the complete endpoint and authentication requirements before writing code.

  • Server URI: Use the scheme and host supplied by the provider, such as http://proxy.example:3128 or socks5://proxy.example:1080.
  • Credentials: Keep the username and password outside source control. Environment variables or a secret manager are safer than literals in a script.
  • Bypass hosts: Add hosts that must connect directly as a comma-separated string, for example localhost,127.0.0.1.
  • Access policy: Confirm that the proxy permits the destination domains, ports, and request volume your job needs.
export PROXY_SERVER='http://proxy.example:3128'
export PROXY_USER='proxy-user'
export PROXY_PASSWORD='use-a-secret-manager'

2. Choose the configuration scope

Browser-level proxy

Set proxy in chromium.launch(), firefox.launch(), or webkit.launch(). Every context and page created by that browser uses the endpoint. This is convenient when one worker handles one network identity.

const browser = await chromium.launch({
  proxy: {
    server: process.env.PROXY_SERVER,
    username: process.env.PROXY_USER,
    password: process.env.PROXY_PASSWORD,
  },
});

const firstContext = await browser.newContext();
const secondContext = await browser.newContext();
// Both contexts use the launch-time proxy.

Context-level proxy

Set proxy in browser.newContext() when only one workflow needs the proxy or when a single browser process must run isolated contexts with different network routes.

const browser = await chromium.launch();

const proxiedContext = await browser.newContext({
  proxy: {
    server: process.env.PROXY_SERVER,
    username: process.env.PROXY_USER,
    password: process.env.PROXY_PASSWORD,
    bypass: 'internal.example.com,localhost',
  },
});

const directContext = await browser.newContext();

const proxiedPage = await proxiedContext.newPage();
await proxiedPage.goto('https://example.com');

const directPage = await directContext.newPage();
await directPage.goto('https://example.com');
Scope Applies to Use it when
Browser launch All contexts in that browser Every capture in a worker should use the same endpoint.
Browser context Pages in one context Different jobs need different routes or isolated credentials.

3. Capture a page through the proxy

Create a page in the configured context, navigate to the target, and capture after the page reaches the state you need. The Playwright screenshot API supports a file path, a full-page image, an element image, clipping, image formats, quality, and an in-memory buffer.

import { chromium } from 'playwright';

const browser = await chromium.launch({
  proxy: {
    server: process.env.PROXY_SERVER,
    username: process.env.PROXY_USER,
    password: process.env.PROXY_PASSWORD,
  },
});

const context = await browser.newContext({
  viewport: { width: 1440, height: 900 },
});
const page = await context.newPage();

await page.goto('https://example.com', {
  waitUntil: 'networkidle',
  timeout: 60_000,
});

// Full scrollable page.
await page.screenshot({
  path: 'page.png',
  fullPage: true,
});

// One element instead of the full page.
const heading = page.locator('h1');
await heading.screenshot({ path: 'heading.png' });

await browser.close();

Wait for the page state you actually need

networkidle can be useful for pages that finish loading their data, but analytics, ads, and live connections may prevent a quiet network. Prefer a known selector or an explicit short delay when the page has a reliable readiness signal.

await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
await page.locator('[data-ready="true"]').waitFor({ state: 'visible', timeout: 30_000 });
await page.screenshot({ path: 'ready.png', fullPage: true });

Inspect requests and responses

A successful top-level navigation does not prove that scripts, fonts, images, or API calls loaded through the proxy. Log failures and selected response statuses while diagnosing a capture.

page.on('requestfailed', request => {
  console.error('REQUEST_FAILED', request.method(), request.url(), request.failure()?.errorText);
});

page.on('response', response => {
  if (response.status() >= 400) {
    console.warn('HTTP_ERROR', response.status(), response.url());
  }
});

await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });

4. Complete runnable example with retries

This worker keeps credentials in environment variables, records failed requests, waits for a selector, and retries transient navigation errors. Retries do not fix an invalid endpoint or a consistently blocked destination, so keep the attempt count small.

import { chromium } from 'playwright';

const target = process.argv[2] ?? 'https://example.com';
const attempts = 2;
let lastError;

for (let attempt = 1; attempt <= attempts; attempt++) {
  const browser = await chromium.launch({
    proxy: {
      server: process.env.PROXY_SERVER,
      username: process.env.PROXY_USER,
      password: process.env.PROXY_PASSWORD,
      bypass: process.env.PROXY_BYPASS,
    },
  });

  try {
    const context = await browser.newContext({ viewport: { width: 1440, height: 900 } });
    const page = await context.newPage();
    page.on('requestfailed', request => {
      console.error('request failed:', request.url(), request.failure()?.errorText);
    });

    await page.goto(target, { waitUntil: 'domcontentloaded', timeout: 60_000 });
    await page.screenshot({ path: 'screenshot.png', fullPage: true, type: 'png' });
    await browser.close();
    process.exit(0);
  } catch (error) {
    lastError = error;
    await browser.close();
    if (attempt < attempts) await new Promise(resolve => setTimeout(resolve, 1_000));
  }
}

console.error(lastError);
process.exit(1);

5. cURL, Python, and Node.js alternatives

The proxy setting belongs to the Playwright browser in the workflow above. The following examples show how to call a screenshot service directly when you do not want to operate a browser.

cURL

curl -G "https://api.screenshotneo.com/v1/shot" \
  -d access_key=YOUR_API_KEY \
  --data-urlencode url=https://stripe.com \
  -o shot.webp

Python

import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
    timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)

Node.js

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 failed: ${res.status}`);
const bytes = Buffer.from(await res.arrayBuffer());
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', bytes));

See the ScreenshotNeo API documentation for request options and response headers.

6. Or skip the browser setup

ScreenshotNeo provides a website screenshot API and MCP server. One GET request returns a PNG, JPEG, WebP, or PDF. Cookie banners, newsletter popups, and chat widgets are removed before the shot; bot checks, blank pages, failed loads, timeouts, and cache hits are not billed. Responses identify the page verdict and billing status with X-Page-Verdict and X-Billed headers. Its MCP server lets Claude, Cursor, and other MCP clients call take_screenshot, get_page_info, and capture_pdf.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

You can still control capture details such as full-page mode, CSS selectors, dark mode, device and viewport settings, retina scale, custom CSS and JavaScript, clicks, waits, blocked resources, headers, cookies, user agent, timezone, geolocation, transparent backgrounds, resizing, caching TTL, signed links, asynchronous jobs, webhooks, bulk capture, and PDF options. Every feature is available on every plan. The free plan includes 1,000 shots each month with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.

7. Runtime proxy versus installation proxy

A proxy used by the browser at runtime is separate from a proxy used to download Playwright’s browser binaries. The installation guide documents setting HTTPS_PROXY for the install command. Do not treat that environment variable as a replacement for the proxy object passed to browser launch or context creation.

HTTPS_PROXY=http://proxy.example:3128 npx playwright install chromium

If an intercepting installation proxy uses a private certificate authority and the download reports a certificate-chain error, Playwright documents supplying the trusted root with NODE_EXTRA_CA_CERTS. Configure this only for the installation process and according to your organization’s certificate policy.

8. Troubleshooting

Symptom Likely cause Fix
Proxy authentication error Wrong username/password, unsupported auth method, or credentials omitted from the selected scope. Verify the endpoint and credentials with the proxy administrator. Keep them in environment variables and confirm the context that creates the page has the proxy.
Navigation times out The proxy cannot reach the host, the host is slow, or the endpoint is overloaded. Log requestfailed events, test a simple destination, increase the navigation timeout only when justified, and check proxy access rules.
HTML loads but images or fonts are missing Subresource requests are blocked, DNS differs through the proxy, or the proxy denies those domains. Inspect failed requests and response status codes. Check asset host allowlists and compare the rendered page with a direct connection.
Unexpected direct traffic A host matches bypass, or the proxy was configured on a different browser/context than the page. Review the comma-separated bypass list and move the setting to the browser launch or exact context that owns the page.
SOCKS endpoint fails The scheme or endpoint is wrong, or the provider does not support the requested SOCKS version. Use the provider’s exact socks5:// URI and confirm its supported version and authentication format.
Screenshot is blank or incomplete Capture happened before client rendering, lazy images loaded, or a consent/modal layer covered content. Wait for a stable selector or required response, scroll or use full-page capture as appropriate, and inspect the page before saving the image.
Browser installation fails behind a corporate proxy The download proxy is separate from runtime routing, or its custom CA is not trusted. Set HTTPS_PROXY for installation and configure NODE_EXTRA_CA_CERTS for the approved root certificate.
Top-level page succeeds but APIs return 403 The destination applies different controls to API or asset hosts. Check response logs, follow the site’s terms, and ask the site owner or network administrator for an authorized route.

9. Performance, reliability, and cost

Performance

  • Launching a fresh browser for every URL adds startup overhead. Reuse a browser process when jobs share a proxy, while creating separate contexts for isolation.
  • Use a smaller viewport and element screenshots when a full-page image is unnecessary.
  • Wait for a specific readiness selector instead of an unbounded network-idle condition on pages with long-lived connections.
  • Proxy distance, DNS resolution, TLS negotiation, and destination response time all affect capture latency. Measure these separately in request logs.

Reliability

  • Validate the proxy with a simple page before processing a large batch.
  • Retry only transient failures, with a short capped delay. Do not retry authentication errors indefinitely.
  • Record the target URL, proxy endpoint identifier, navigation result, failed requests, and screenshot path without logging secrets.
  • Check the image itself. A 200 response from the destination does not prove that every visual resource rendered.

Cost

Playwright itself does not define a proxy provider’s pricing. Proxy charges, browser compute, bandwidth, storage, and any screenshot service fees are separate; verify current terms with the provider you choose. ScreenshotNeo bills only clean shots; bot checks, blank pages, failed loads, timeouts, and cache hits cost nothing. Its plans are Free (1,000/month), Starter ($5 for 3,000), Growth ($15 for 15,000), Pro ($39 for 60,000), Scale ($99 for 250,000), and Business ($249 for 1,000,000); yearly billing gives two months free.

10. Security and operational checklist

  • Store proxy credentials in a secret manager or environment, never in committed code or screenshot filenames.
  • Redact credentials, authorization headers, cookies, and signed URLs from logs.
  • Use the narrowest bypass list possible; bypassing a host sends it outside the proxy route.
  • Confirm that the proxy and capture workflow are authorized for the sites and data involved.
  • Limit screenshots and response artifacts to the retention period your policy allows.
  • Rotate credentials when a worker, CI log, or source repository may have exposed them.

FAQ

Can one Playwright browser use several proxies?

Use separate browser contexts when your Playwright version and workflow support context-scoped proxies, or launch separate browser instances for strict isolation. The browser-level setting applies to all contexts in that browser.

Does a proxy make a screenshot anonymous?

No. It changes the network route. The destination can still observe browser characteristics, cookies, request headers, and other signals, and its policies still apply.

Should I use a browser proxy or a screenshot API?

Use Playwright when you need browser automation, custom interaction, or detailed control over a local workflow. Use a managed API when you want a single request without maintaining browser binaries, proxy routing, retries, and capture infrastructure.

What should I verify first when a capture fails?

Confirm the endpoint scheme, credentials, bypass list, destination access, and failed subresource requests. Then verify that the screenshot waits for the page state your target actually needs.

For Playwright’s authoritative syntax and behavior, consult the proxy documentation, browser API reference, and screenshot guide.