Skip to content

Preserve requested HTTP with managed Chrome policies - #45

Merged
mandatoryprogrammer merged 3 commits into
mandatoryprogrammer:mainfrom
bconik:fix/chrome-http-navigation-timeout
Oct 4, 2026
Merged

mandatoryprogrammer merged 3 commits into
mandatoryprogrammer:mainfrom
bconik:fix/chrome-http-navigation-timeout

Conversation

@bconik

@bconik bconik commented Jul 27, 2026 •

Copy link
Copy Markdown

Chrome can upgrade a caller-requested HTTP navigation to HTTPS while Thermoptic waits for a response matching the original HTTP URL, eventually returning a 502 timeout. Upgrading the page used to establish an HTTP fetch context can also prevent the intended request from completing.

Install mandatory managed policies in the dedicated Chrome image: HttpsUpgradesEnabled: false disables opportunistic upgrades, and HttpsOnlyMode: "disallowed" disables HTTPS-First, including strict or balanced settings retained in an existing profile. Using both supported policies covers the independent upgrade mechanisms.

The image copies chrome/policies/http_request_scheme.json into Chrome's managed-policy directory. Operator instructions and the behavior change are documented in _readme/internal-notes.md; rebuild/recreate the Chrome service to apply the policy. The main README, request-handling code, dependencies, and lockfile are unchanged.

Keeping caller-requested HTTP on HTTP is intentional proxy behavior and can leave traffic plaintext that Chrome would otherwise upgrade. Explicit HTTPS, certificate validation, HSTS, and server-issued redirects remain enabled. HSTS-covered HTTP URLs can still return a 307 upgrade redirect. Separately supplied Chrome instances need equivalent configuration in their own dedicated environment.

Validation

Both docker-build (ubuntu-latest) and compose-e2e passed on f9754ff8419f764b7ade6b31c0285a19955c6334. Successful CI run.

Used existing dependencies with Node 18.20.4 and Chrome 152.0.7977.64. Real browser requests went through an isolated local forward proxy to local HTTP/HTTPS fixtures with public-style hostnames; no third-party target was required. The policy directory was mounted into isolated Chrome processes without changing the host browser's policies.

  • Reproduced the original 30-second timeout and the remaining timeout with HTTPS-First enabled under the original feature-flag change.
  • Verified the final managed policies on a fresh profile and copies of an existing profile with strict or balanced HTTPS-First enabled. HTTP navigation and HTTP fetch returned 200 with the expected plaintext fixture response in all three cases.
  • Explicit HTTPS returned 200; a server-issued HTTP redirect retained its 301 and correct Location.
  • After learning HSTS from the HTTPS fixture, an HTTP request returned a 307 pointing to HTTPS, without sending a plaintext request to the server.
  • An untrusted TLS certificate was rejected; Chrome reported net::ERR_CERT_AUTHORITY_INVALID and Thermoptic returned 502. Trusted fixture cases used a temporary certificate-specific SPKI allowance; the certificate-rejection case ran without that allowance.
  • chrome://policy reported both policies as Platform/Machine/Mandatory with status OK in all tested profiles.
  • JSON parsing, shell syntax, and git diff --check passed. No dependencies were installed locally and no test files were added to the repository.

The temporary verification runner used node check.mjs fresh persisted-strict persisted-balanced untrusted-cert; it launches disposable headless Chrome processes with isolated policy mounts and profiles, serves the local fixtures, checks status/body/redirect behavior, and stops its own processes. TLS/HTTP fingerprint parity was not measured.

noreply and others added 2 commits July 27, 2026 19:15
Every plain-HTTP request proxied through thermoptic times out and returns
a 502, regardless of the target site.

Chrome's HttpsUpgrades feature silently rewrites top-level http://
navigations to https:// before making the real request. cdp.js's
manual_browser_visit() registers its CDP Fetch domain interception pattern
against the original http:// URL and then calls Page.navigate with that
same URL, expecting Fetch.requestPaused to fire once the response comes
back. Since the actual outgoing request is for the upgraded https:// URL,
the interception pattern never matches, Fetch.requestPaused never fires,
and the request sits until the internal timeout fires, surfacing to the
client as a 502.

Adding HttpsUpgrades to Chrome's --disable-features list stops the silent
rewrite so the navigation matches the interception pattern and completes
normally.
@mandatoryprogrammer

Copy link
Copy Markdown
Owner

This is a really good catch that was missed because the behavior is different for localhost HTTP checks which I was doing previously, working on the fix now and will merge.

@mandatoryprogrammer mandatoryprogrammer changed the title Disable Chrome's HttpsUpgrades feature to fix plain-HTTP proxying Preserve requested HTTP with managed Chrome policies Oct 4, 2026
@mandatoryprogrammer
mandatoryprogrammer merged commit 813f8c2 into mandatoryprogrammer:main Oct 4, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants