From 0ccbbd6a13e96f8830eeb40a64f27841d2a0d403 Mon Sep 17 00:00:00 2001 From: Raphael Malikian Date: Tue, 3 Nov 2026 16:29:27 -0800 Subject: [PATCH] fix: do not send an empty sec-ch-ua header for empty or unparseable user agents Signed-off-by: Raphael Malikian --- crawl4ai/async_configs.py | 7 ++++- tests/test_browser_config_empty_sec_ch_ua.py | 33 ++++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 tests/test_browser_config_empty_sec_ch_ua.py diff --git a/crawl4ai/async_configs.py b/crawl4ai/async_configs.py index 15789be1e..c01253533 100644 --- a/crawl4ai/async_configs.py +++ b/crawl4ai/async_configs.py @@ -1166,7 +1166,12 @@ def __init__( pass self.browser_hint = UAGen.generate_client_hints(self.user_agent) - self.headers.setdefault("sec-ch-ua", self.browser_hint) + # An empty user agent (or one with no recognizable browser) yields an + # empty client-hints value; sending an empty sec-ch-ua header misleads + # servers, so only auto-add the header when there is a hint to send. + # A caller-provided sec-ch-ua is still respected via setdefault. + if self.browser_hint: + self.headers.setdefault("sec-ch-ua", self.browser_hint) # Set appropriate browser management flags based on browser_mode if self.browser_mode == "builtin": diff --git a/tests/test_browser_config_empty_sec_ch_ua.py b/tests/test_browser_config_empty_sec_ch_ua.py new file mode 100644 index 000000000..360b40ed9 --- /dev/null +++ b/tests/test_browser_config_empty_sec_ch_ua.py @@ -0,0 +1,33 @@ +"""Regression tests for crawl4ai issue #2493 (part: empty sec-ch-ua). + +BrowserConfig(user_agent="") keeps the browser's native User-Agent, but +__init__ used to auto-add an empty "sec-ch-ua" header to every request. +An empty client-hints header must not be sent; a caller-provided value +must still be respected. +""" + +from crawl4ai.async_configs import BrowserConfig + + +class TestEmptySecChUa: + def test_empty_user_agent_adds_no_sec_ch_ua(self): + config = BrowserConfig(user_agent="") + assert config.browser_hint == "" + assert "sec-ch-ua" not in config.headers + + def test_unparseable_user_agent_adds_no_sec_ch_ua(self): + config = BrowserConfig(user_agent="MyBot/1.0") + assert config.browser_hint == "" + assert "sec-ch-ua" not in config.headers + + def test_default_user_agent_still_adds_sec_ch_ua(self): + config = BrowserConfig() + assert config.browser_hint != "" + assert config.headers["sec-ch-ua"] == config.browser_hint + + def test_caller_provided_sec_ch_ua_is_respected(self): + config = BrowserConfig( + user_agent="", + headers={"sec-ch-ua": '"Chromium";v="156"'}, + ) + assert config.headers["sec-ch-ua"] == '"Chromium";v="156"'