[DOCS/BUG] Browser automation tools (Playwright/Puppeteer) incompatible with web sandbox proxy

Status Open
Maintainer reply None cached
Activity 12 comments · opened Nov 17, 2025

Summary

Browser automation tools like Playwright, Puppeteer, and Selenium cannot run in the Claude Code web sandbox due to the security proxy not supporting HTTPS CONNECT tunneling. This is a fundamental architectural limitation that should be documented to save users time.

Environment

  • Claude Code: Web sandbox (claude.ai/code)
  • Tool: Playwright v1.49.1 with Chromium v140.0.7339.16
  • Test Date: 2025-11-16

Problem Description

The web sandbox's security proxy provides network isolation by mediating all outbound traffic through an HTTP proxy (visible as HTTPS_PROXY environment variable with JWT-based authentication). However, this proxy does not support the HTTP CONNECT method required by browsers for HTTPS tunneling.

Why This Matters

  • Browser automation tools (Playwright, Puppeteer, Selenium) require CONNECT tunneling to access HTTPS sites
  • The current proxy architecture only supports direct HTTP requests (e.g., curl, requests library)
  • Users may waste significant time trying to configure browser automation in the sandbox

Reproduction Steps

  1. Install Playwright in web sandbox:

``bash
uv run playwright install chromium
``

  1. Run any Playwright script accessing HTTPS sites:

```python
from playwright.sync_api import sync_playwright

with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
page = browser.new_page()
page.goto('https://example.com') # Fails here
browser.close()
```

  1. Result: ERR_TUNNEL_CONNECTION_FAILED

Error Messages Encountered

  • net::ERR_TUNNEL_CONNECTION_FAILED - Most common (HTTPS sites)
  • net::ERR_NO_SUPPORTED_PROXIES - When credentials in proxy URL
  • HTTP 401 - When testing HTTP (not HTTPS) sites with JWT auth

Technical Analysis

What Works ✅

  • HTTP tools like curl, requests, httpx (direct requests)
  • Playwright installation
  • All non-browser automation

What Doesn't Work ❌

  • Any browser (Chromium, Firefox, WebKit) accessing HTTPS sites
  • Playwright, Puppeteer, Selenium browser automation
  • Any tool requiring CONNECT tunneling

Root Cause

From Anthropic's sandboxing docs:

"Network isolation: All outbound internet traffic passes through a proxy for security and abuse prevention purposes."

The security proxy:

  • Uses JWT-based authentication in URL format
  • Supports direct HTTP/HTTPS requests (standard proxy behavior)
  • Does NOT support HTTP CONNECT method (browser tunneling)

This is by design for security, but creates incompatibility with browser automation.

Proposed Solutions

Option 1: Documentation (Immediate) ⭐

Add to sandbox documentation:

Browser Automation Limitations: Browser automation tools (Playwright, Puppeteer, Selenium) are not supported in the web sandbox environment. The security proxy does not support HTTPS CONNECT tunneling required by browsers. For web scraping, use HTTP libraries (requests, httpx) with HTML parsing (BeautifulSoup, lxml) instead, or run browser automation locally.

Option 2: Feature Enhancement (Future)

  • Add CONNECT support to sandbox proxy (security implications to evaluate)
  • Provide alternative browser automation pathway
  • Offer Playwright MCP integration that works within sandbox constraints

Option 3: Workaround Guidance

Document recommended alternatives:

  • Use requests + BeautifulSoup for HTML scraping
  • Use httpx with async support
  • Run browser automation locally, import results to sandbox

Impact

User Experience:

  • Users attempting browser automation waste hours debugging
  • No clear error message indicating architectural limitation
  • Documentation doesn't mention this restriction

Use Cases Affected:

  • Web scraping jobs
  • End-to-end testing
  • Screenshot/PDF generation
  • Form automation

Related Issues

  • #2256 - Tunnel proxy issues (corporate proxies)
  • #1383 - Playwright MCP failures
  • #5636 - Browser automation tools failing to launch

Recommendation

At minimum, update documentation to clearly state browser automation tools won't work in web sandbox and suggest alternatives. This would save users significant debugging time and set proper expectations.

---

Category: Documentation / Known Limitation
Priority: Medium (affects user experience, not a critical bug)
Effort: Low (documentation update) to High (architectural change)

View original on GitHub ↗

9 Comments

ian-klopper · 9 months ago

Adding my use case: I want cloud agents to test preview deployments (e.g., Vercel previews) using Browserbase/Stagehand before committing. Even though Browserbase runs browsers remotely, agents still need WebSocket/CDP connections to control them - which gets blocked by the current proxy. This workflow works locally but not in web sessions, which defeats the purpose of autonomous cloud agents.

iSuslov · 8 months ago

In fact very important issue. Does anyone has an elegant workaround?

KingMob · 8 months ago

I'm unable to run my automated Playwright tests in the sandbox, despite excluding the commands used.

I'm going to have to stop using the sandbox for this atm, which means my alternatives are either more dangerous, or more annoying.

github-actions[bot] · 7 months ago

This issue has been inactive for 30 days. If the issue is still occurring, please comment to let us know. Otherwise, this issue will be automatically closed in 30 days for housekeeping purposes.

vj-cyntexa · 7 months ago

It's a critical issue we need a fix or a workaround or get it documented atleast.

mpasternak · 5 months ago

Would be great to have this.

wuservices · 5 months ago

We hit this exact problem running Playwright E2E tests in Claude Code's web sandbox and found a working set of workarounds. Sharing the strategy and key discoveries — the specifics will vary by framework, but the pattern is universal.

My main motivation was to allow Claude Code to build, and validate E2E tests when running in the cloud to make it easier to run 10s of high quality concurrent sessions. I also have it take screenshots with read file to inline into the chat, and post images to embed into the PRs for quick review.

After a lot of trial and error, Claude found a workaround that lets Playwright tests run against a localhost dev server, including loading external resources (fonts, CDNs, APIs). This also installs Chromium since I needed the latest version to match my Playwright version vs the pre-installed versions.

While it fixed my issue, _the facts may not be completely right, as Claude cooked up the summary below_ of its own workaround. Initially it did brute force a ton of options in, but I then had it test all combinations to figure out what was really needed to reduce the noise on flags that weren't impactful.

-----

The Core Insight

The browser can't reach the internet, but Node.js can (via the egress proxy). Playwright's page.route() API lets you intercept requests at the network layer before Chromium tries to resolve DNS. So the strategy is: intercept external requests in Playwright, re-fetch them through Node.js using undici.ProxyAgent, and hand the response back to the browser.

Browser request → page.route() intercept → Node.js fetch (via ProxyAgent) → Egress proxy → Internet
                                                         ↓
                                                  Response back to browser

This means your tests run against a localhost dev server, with external resources (fonts, CDN scripts, API calls) transparently proxied through Node.js.

The Three Things You Need

1. A Playwright route interceptor

Use page.route() to match all non-localhost URLs. In the handler, fetch the URL using undici.ProxyAgent pointed at process.env.HTTPS_PROXY, strip hop-by-hop headers, and route.fulfill() with the response. Wire this into a custom Playwright fixture so every test gets it automatically.

2. A Node.js proxy bootstrap for your dev server

Node 22's built-in fetch() (undici) does not honor HTTP_PROXY/HTTPS_PROXY env vars by default. If your dev server makes any server-side fetches (SSR, API routes, font loading), they'll fail with EAI_AGAIN. Fix this with a small preload script that calls undici.setGlobalDispatcher(new EnvHttpProxyAgent()), loaded via NODE_OPTIONS="--require ./proxy-bootstrap.cjs" in your Playwright config's webServer.env.

3. A no_proxy override for playwright install

The container sets no_proxy=*.googleapis.com,*.google.com,... by default, which tells Node.js "bypass the proxy for these domains and connect directly." But DNS is completely broken without the proxy, so "connect directly" = EAI_AGAIN.

Playwright downloads Chromium from cdn.playwright.dev → redirects to storage.googleapis.com → matches the *.googleapis.com exclusion → bypasses proxy → DNS fails.

The fix: override no_proxy to just localhost when installing:

no_proxy="localhost" npx playwright install chromium

Important details:

  • Only lowercase no_proxy matters. Both undici and proxy-from-env (Playwright's library) prefer lowercase over uppercase NO_PROXY. Setting only uppercase does nothing when lowercase is already set.
  • no_proxy="localhost" is sufficient — no need for 127.0.0.1.
  • Blank no_proxy="" doesn't work — empty string is falsy in JS, so no_proxy || NO_PROXY falls through to the uppercase default which still has *.googleapis.com.
  • npx playwright test may not need this override if your Playwright config injects the proxy bootstrap via NODE_OPTIONS into the dev server process (it sets no_proxy internally).

Playwright Config Tips

  • Launch Chromium with args: ['--no-proxy-server'] so the browser doesn't try to use the proxy itself (your route interceptor handles it at the application level).
  • In your webServer config, keep proxy env vars for the dev server process (it needs them to reach external APIs), but inject the proxy bootstrap via NODE_OPTIONS.
  • Always use pnpm exec playwright (or npx playwright) — never the bare playwright command. The container has a globally installed version that may not match your project's @playwright/test, causing "browser not found" errors.

Google Fonts Gotcha

If your app uses Google Fonts, they'll 403 from this environment — Google blocks requests from datacenter/proxy IPs. Bunny Fonts is a privacy-focused, open-source font CDN that mirrors the Google Fonts API with identical URL structure — same query params, same CSS format, same font files. It's a one-line URL rewrite away: swap fonts.googleapis.comfonts.bunny.net in your route interceptor. Also rewrite URLs inside CSS response bodies, since Google Fonts CSS contains embedded url() references pointing back to fonts.gstatic.com for the actual font files.

Quick Start

# 1. Install project deps
npm install

# 2. Install Chromium (only step that needs no_proxy override)
no_proxy="localhost" npx playwright install chromium

# 3. Run tests (no override needed if config injects proxy bootstrap)
npx playwright test

Limitations

  • Localhost only for top-level navigation. Your app must run as a dev server. You can't page.goto('https://example.com') directly — the interceptor proxies sub-resources loaded by the page.
  • WebSocket connections to external hosts require a more involved CONNECT tunnel through Node.js (doable but more complex).
  • Bare playwright screenshot of external URLs won't work without additional plumbing.

This pattern — page.route() + undici.ProxyAgent + no_proxy override — has been reliable for us running a full Nuxt + Playwright E2E suite in the sandbox. Hope it unblocks others hitting this wall.

WestonThayer · 2 months ago

Weirdly this was working on June 19 and 22. It was working earlier today, then suddenly stopped and Chrome started hitting net::ERR_CONNECTION_CLOSED.

Rob-Makappen · 1 month ago

This is one of three issues documenting the same underlying gap (with #15583 and #73564): there's no way to run a browser in Claude Code cloud sessions, DIY or otherwise.

I've filed the feature-level ask at #75632. It proposes an opt-in, pre-installed headless Chromium scoped to localhost first, which sidesteps the CONNECT tunneling limitation described here entirely: verifying a dev server inside the sandbox never touches the egress proxy. External browsing is framed as a separate phase 2 given the security trade-offs you'd rightly flag with CONNECT support.

If browser verification in cloud sessions matters to your workflow, a 👍 over there helps it get triaged.

Showing cached comments. Read the full discussion on GitHub ↗