A VPN can carry a browser's traffic to a proxy server and still leave the browser unable to use that proxy, because a route and a credential are different things. Headless automation makes the gap harder to see, since there is no window and no prompt to notice. The failure usually appears as a hang or a generic navigation error, and the first instinct is to blame the tunnel. The checklist below separates the four things that have to be true before a headless request through an authenticated proxy can succeed.

Two conversations, two credentials

Two conversations happen, and they belong to different parties. The browser first connects to the proxy listener, which may challenge it for credentials; the proxy then makes or permits the onward connection to the destination. A VPN can carry the first leg without being the endpoint that challenges the browser, and it has nothing to say about the second.

HTTP 407 is the signal that the client must authenticate for the proxy request, carried in Proxy-Authenticate with the answer expected in Proxy-Authorization. That is not the destination website's login, and mixing the two is the most common source of confusion here.

So the four checks are: which proxy the browser actually selected, who answers the 407, which network the browser process is on, and how the credential is delivered without a human. Each one can fail independently, and each failure looks similar from outside. Work them in order, because a later check cannot compensate for an earlier one. A proxy that was never selected cannot be authenticated, and a credential that only exists on your laptop cannot reach a container.

Check 1: which proxy is actually selected

Start with selection, because a browser that ignores your proxy setting never reaches the challenge at all. In Chrome the flag is --proxy-server=proxy.example:3128, with --proxy-bypass-list for exclusions, and Selenium exposes the same thing through its browser options. A PAC file is a different mechanism with its own failure modes.

Then confirm from inside the running browser's environment rather than from your shell. An independent probe with the same proxy is curl -x http://proxy.example:3128 -v https://example.com, and its verbose output shows whether the CONNECT succeeded, what Proxy-Authenticate came back, and whether a 407 was returned. If curl gets a 407 and the browser hangs, the credential path is the problem rather than the network. If curl succeeds and the browser does not, the browser is not using the proxy you think it is.

Check 2: who answers the 407

A 407 has an owner, and naming it decides where the fix goes. If the challenge comes from the corporate proxy, the fix is a credential the browser can present unattended. If it comes from an upstream proxy that the first one forwards to, the credential belongs to that second hop and the first proxy may be relaying the challenge unchanged. Read the authentication scope and host in the Proxy-Authenticate header, because they name the box that is asking.

Preserve that distinction in whatever you log. A proxy challenge, a refused onward connection, a website sign-in and a VPN transport timeout are four different failures with four different owners, and one generic error message destroys the information you need.

Check 3: the credential path

The credential itself is where most unattended setups break. Selenium can set a proxy host and port, but HTTP proxy authentication is not a portable part of that configuration, and credentials in the proxy URL are not a mechanism you can rely on across browsers and driver versions. The supported options are the ones your actual stack documents: a browser extension that answers the challenge, a local forwarding proxy that adds the header and is reachable only from the job, or a proxy that authenticates the workload by other means. Pick one deliberately, and check it against the exact browser, driver and Selenium versions you will run.

Keep the credential out of the places that leak it. Command lines are visible in process listings, screenshots end up in test reports, browser traces and HAR files capture headers, and committed test configuration is forever. Use the job's secret delivery mechanism and its redaction rules, and scope the proxy account to the destinations or role it needs.

If the organisation offers a permitted direct VPN route instead of a mandatory proxy, compare the two designs explicitly. Do not quietly disable the proxy or imitate another client's authentication to get around policy.

Check 4: the browser process's own network context

This is the check people skip. A browser launched by a worker, a container or a remote Selenium Grid node uses that environment's networking, so your laptop's VPN connection and your shell's proxy variables describe a path the browser may never touch.

Record five facts for the failing job: the browser host, the configured proxy, the initial resolver path, the selected route and the owner of the authentication. Then make one harmless authorised request from that exact context, with normal destination TLS verification and application authorisation left on. A headless browser that starts successfully proves neither proxy admission nor VPN coverage, and a screenshot of a blank page proves nothing at all. The request that returns 200 through the whole chain is the only evidence that counts.

The finished workflow has three properties worth checking as a set. There is a supported route from the browser host to the proxy, an explicit intermediary policy that names the proxy and the destinations, and a credential lifecycle that does not depend on an operator answering an invisible prompt. If any of the three depends on a person, the job is not unattended yet, whatever the exit code says. Write the five facts down before the next person inherits the pipeline.

អត្ថបទពាក់ព័ន្ធ