← All guides
TROUBLESHOOTING · 5 MIN READ

Why Browser Automation Works Locally but Fails in Cloud

Trace a cloud browser failure by separating runtime, destination policy, proxy routing and page validation.

“It works on my laptop” tells you where one run succeeded. It does not identify the variable that changed in a cloud worker. The useful comparison keeps the URL, browser version, account state, timing and automation steps fixed while recording runtime, exit route, response and extracted content separately.

Build a paired failure record

For each run, record the following fields without storing cookies, passwords or proxy credentials:

Field Local run Cloud run
Browser and library version exact installed versions exact deployed versions
Target and final URL canonical URL canonical URL
Proxy route direct or proxy identifier direct or proxy identifier
HTTP outcome status or exception category status or exception category
Content check expected marker present expected marker present

The split matters. A cloud browser can reach the proxy but receive a destination 403. It can also open a page with status 200 and receive a challenge or login document. Browser Use issue reports are useful for observing failure patterns, but an issue is not proof that every cloud deployment behaves the same way.

Check the browser path before changing providers

Start with a permitted, stable URL and a small run. In Playwright, capture only sanitized diagnostics:

import os
from playwright.sync_api import Error, TimeoutError, sync_playwright

proxy = None
if os.environ.get("PROXY_SERVER"):
    proxy = {"server": os.environ["PROXY_SERVER"]}
    if os.environ.get("PROXY_USER"):
        proxy.update(username=os.environ["PROXY_USER"],
                     password=os.environ["PROXY_PASSWORD"])

phase = "launch"
browser = None
with sync_playwright() as p:
    try:
        browser = p.chromium.launch(headless=True, proxy=proxy)
        page = browser.new_page()
        phase = "navigation"
        response = page.goto("https://example.com/",
                             wait_until="domcontentloaded", timeout=25_000)
        phase = "content_check"
        body = page.text_content("body", timeout=5_000) or ""
        print({"route": "proxy" if proxy else "direct",
               "status": response.status if response else None,
               "has_example_marker": "Example Domain" in body})
    except TimeoutError:
        print({"phase": phase, "failure": "timeout"})
    except Error:
        print({"phase": phase, "failure": "browser_error"})
    finally:
        if browser:
            browser.close()

Run the same probe in the cloud image. Leave PROXY_SERVER unset for the direct baseline. For the proxy run, supply a trusted host and port through PROXY_SERVER, with PROXY_USER and PROXY_PASSWORD supplied by your runtime secret manager when required. The timeouts are milliseconds. The script reports the failing phase and suppresses raw exception messages, which can contain connection details. A generic browser_error still needs a separate DNS, TLS or proxy-authentication check; it is not a diagnosis of the supplier.

If the browser version or installed dependencies differ, fix that before changing the network. Do not put credentials in page headers or screenshots. Playwright’s network documentation describes proxy configuration and request inspection.

Read the result by failure boundary

Observation First hypothesis Useful next test
Launch fails image or browser dependency compare installed browser and library versions
Connection timeout route, firewall or deadline test the proxy endpoint with a bounded probe
407 proxy authentication check endpoint, auth scheme and secret binding
403 or challenge after navigation destination policy compare content and account context, then stop broad retries
200 with no expected marker wrong page or challenge record final URL, title and a safe body marker

A residential proxy can change egress identity, but it cannot grant permission, reproduce a logged-in account or guarantee that a destination accepts automation. If the same cloud image fails through direct access and through a configured proxy, the browser or target policy may be the real bottleneck. If only one route fails, compare its authentication, geography and session lifetime.

Decide whether a proxy belongs in the fix

Use a proxy when the authorized job has a defined network requirement, such as a regional page check or separating worker egress. Keep the browser image, target sample, pacing and validation rule fixed. Compare accepted records and failure categories over the same small batch. Do not call a changed screenshot an improvement until the expected fields pass validation.

For a network-specific diagnostic, read Proxy Error 407 troubleshooting and Playwright proxy setup. If the job needs rendered extraction rather than a network route, compare that requirement with the web scraping API versus proxy guide.

Sources and further reading

Sign in ↗

Keep reading

Amazon Scraper API vs Proxy: Choose a Product Data Source

Distinguish authorized Amazon APIs, licensed product data and proxy-based page checks by record quality and access rights.

Read guide →

Apify Custom Proxy Setup: Use Your Own Residential Route

Connect a buyer-owned proxy to an Apify Actor, keep the session boundary clear, and validate records instead of counting requests.

Read guide →

Australia Residential Proxies: Verify the AU Exit and State-Level Result

Separate Australian egress from en-AU content, AUD pricing, GST display, postcode validation and the state delivery context.

Read guide →