Describe the bug
Under CEF 149, any cross-origin subresource request (script, fetch, etc.) whose URL returns an HTTP 3xx redirect is aborted with net::ERR_INVALID_ARGUMENT. The redirect response is received, but the redirected request is never sent, so the resource silently fails to load. This affects any embedder that installs request handling (returns a CefResourceRequestHandler), which arms CEF's net_service TrustedHeaderClient. It is not specific to any URL characters: a redirect to a plain URL fails identically, while a direct (non-redirected) request to the same final URL succeeds. The trigger is the redirect itself.
To Reproduce
- Run cefclient from the official CEF 149 binary distribution (cefclient returns a CefResourceRequestHandler by default).
- Load a page that requests a cross-origin subresource whose URL HTTP-redirects. Public example, a page containing: <script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script> (that URL returns 302 to https://cdn.tailwindcss.com/3.4.17?plugins=forms@0.5.10,container-queries@0.1.1).
- Watch the network (DevTools, or a NetLog capture).
- See error: the redirect target fails with Failed to load resource: net::ERR_INVALID_ARGUMENT; the script never runs (window.tailwind is undefined).
Expected behavior
The 302 is followed and the subresource loads, exactly as in Google Chrome 149 and CEF 122. Instead the redirected request is rejected with net::ERR_INVALID_ARGUMENT before it is sent.
Screenshots
A NetLog capture (mode: Everything) shows the original URL_REQUEST reaching URL_REQUEST_DELEGATE_RECEIVED_REDIRECT, with no URL_REQUEST ever created for the redirect target — the rejection happens at the factory, before any net-stack request.
Versions
- OS: Windows 11 (x64)
- CEF Version: 149.0.4 (g2f1bfd8) / Chromium 149.0.7827.156 (source commit 2f1bfd8)
Additional context
Does the problem reproduce with cefclient/cefsimple at the same version? Yes. The defect is in shared libcef code (libcef/browser/net_service/proxy_url_loader_factory.cc), not embedder code, and the default cefclient returns a CefResourceRequestHandler, which arms the factory-level TrustedHeaderClient that triggers it. Regression confirmed against the sample app: cefclient 122.0.x follows the same 302 and loads the resource; 149.0.4 fails.
Does the problem reproduce with Google Chrome at the same version? No. Stock Chrome / Chromium 149 follows the redirect and loads the resource.
Root cause (verified from source + NetLog + a patched libcef build):
- CEF arms the TrustedHeaderClient at the factory level (InterceptedRequest::Restart → current_request_uses_header_client_), independent of per-request GetResourceRequestHandler.
- OnBeforeSendHeaders folds the network-managed headers into the request (request_.headers = headers;), including the browser-set Sec-Fetch-* (Site/Mode/Dest, and in M149 Sec-Fetch-Storage-Access).
- On a redirect, CEF re-submits the whole request through CorsURLLoaderFactory::CreateLoaderAndStart (FollowRedirect→Restart→ContinueAfterIntercept), re-running IsValidRequest.
- IsValidRequest rejects it via ContainsForbiddenSecurityHeader ("Forbidden Sec- header from renderer"): the folded Sec-Fetch-* look renderer-set. That guard is gated by features::kRestrictForbiddenSecurityHeaders, FEATURE_ENABLED_BY_DEFAULT in M149 and absent in M122 — hence the regression. (AreRequestHeadersSafe is not the offender; it passes.)
- Stock Chromium follows redirects in place (CorsURLLoader::FollowRedirect), validates only modified headers, and strips/re-adds sec-fetch-* itself (MaybeRemoveSecHeaders); it never re-submits the folded request through the factory.
Proposed fix (diff below): in ContinueToBeforeRedirect, extend remove_headers (already strips Cookie) to also strip, before RedirectUtil::UpdateHttpRequest, the kUnsafeHeaders names + any Proxy- header (covers AreRequestHeadersSafe) and any Sec-Fetch-* header (covers ContainsForbiddenSecurityHeader; re-added by SetFetchMetadataHeaders). Sec-CH-* intentionally not stripped. Verified at runtime: the 302 loads, zero ERR_INVALID_ARGUMENT. Broader alternative: follow on the existing target_loader_ instead of re-submitting (changes the re-interception contract, so the strip is the conservative choice).
Describe the bug
Under CEF 149, any cross-origin subresource request (script, fetch, etc.) whose URL returns an HTTP 3xx redirect is aborted with net::ERR_INVALID_ARGUMENT. The redirect response is received, but the redirected request is never sent, so the resource silently fails to load. This affects any embedder that installs request handling (returns a CefResourceRequestHandler), which arms CEF's net_service TrustedHeaderClient. It is not specific to any URL characters: a redirect to a plain URL fails identically, while a direct (non-redirected) request to the same final URL succeeds. The trigger is the redirect itself.
To Reproduce
Expected behavior
The 302 is followed and the subresource loads, exactly as in Google Chrome 149 and CEF 122. Instead the redirected request is rejected with net::ERR_INVALID_ARGUMENT before it is sent.
Screenshots
A NetLog capture (mode: Everything) shows the original URL_REQUEST reaching URL_REQUEST_DELEGATE_RECEIVED_REDIRECT, with no URL_REQUEST ever created for the redirect target — the rejection happens at the factory, before any net-stack request.
Versions
Additional context
Does the problem reproduce with cefclient/cefsimple at the same version? Yes. The defect is in shared libcef code (libcef/browser/net_service/proxy_url_loader_factory.cc), not embedder code, and the default cefclient returns a CefResourceRequestHandler, which arms the factory-level TrustedHeaderClient that triggers it. Regression confirmed against the sample app: cefclient 122.0.x follows the same 302 and loads the resource; 149.0.4 fails.
Does the problem reproduce with Google Chrome at the same version? No. Stock Chrome / Chromium 149 follows the redirect and loads the resource.
Root cause (verified from source + NetLog + a patched libcef build):
Proposed fix (diff below): in ContinueToBeforeRedirect, extend remove_headers (already strips Cookie) to also strip, before RedirectUtil::UpdateHttpRequest, the kUnsafeHeaders names + any Proxy- header (covers AreRequestHeadersSafe) and any Sec-Fetch-* header (covers ContainsForbiddenSecurityHeader; re-added by SetFetchMetadataHeaders). Sec-CH-* intentionally not stripped. Verified at runtime: the 302 loads, zero ERR_INVALID_ARGUMENT. Broader alternative: follow on the existing target_loader_ instead of re-submitting (changes the re-interception contract, so the strip is the conservative choice).