Skip to content

Fix swapped Sec-Fetch-* header values - #45

Open
devvyt wants to merge 1 commit into
daijro:mainfrom
devvyt:fix-sec-fetch-header-values
Open

devvyt wants to merge 1 commit into
daijro:mainfrom
devvyt:fix-sec-fetch-header-values

Conversation

@devvyt

@devvyt devvyt commented Sep 4, 2026

Copy link
Copy Markdown

Problem

HeaderGenerator emits all four Sec-Fetch-* headers with values that are invalid for the header they appear on. Every generated header set is affected.

Headers from HeaderGenerator().generate(browser='chrome'), sent with requests to https://httpbin.org/headers:

Sec-Fetch-Dest: navigate
Sec-Fetch-Mode: same-site
Sec-Fetch-Site: ?1
Sec-Fetch-User: document
Header Valid values Currently sends
Sec-Fetch-Mode cors, navigate, no-cors, same-origin, websocket same-site
Sec-Fetch-Dest document, empty, image, script, … navigate
Sec-Fetch-Site cross-site, same-origin, same-site, none ?1
Sec-Fetch-User ?1 (boolean, only sent when true) document

Each value is valid, but on the wrong header.

Fix

Corrects the values in both constants to match the spec and upstream fingerprint-suite's assignment (site → same-site, mode → navigate, user → ?1, dest → document). Keys unchanged.

Sec-Fetch-Site is context-dependent in a real browser (none on a fresh top-level navigation), so this keeps upstream's same-site rather than change behavior beyond the swap.

Verification

python - <<'EOF'
import collections
from browserforge.headers import HeaderGenerator

want = {'sec-fetch-mode': 'navigate', 'sec-fetch-dest': 'document',
        'sec-fetch-site': 'same-site', 'sec-fetch-user': '?1'}
seen = collections.defaultdict(collections.Counter)
hg = HeaderGenerator()
for _ in range(200):
    h = {k.lower(): v for k, v in hg.generate(browser='chrome').items()}
    if 'sec-fetch-mode' in h:          # pre-Chrome-76 UAs correctly omit these
        for k in want:
            seen[k][h[k]] += 1

for k, v in sorted(want.items()):
    got = dict(seen[k])
    print(f"{k:16} {got}  {'OK' if set(got) == {v} else 'FAIL, expected ' + v}")
EOF

Passes on this branch, fails on main. Safari is unaffected.

The four Sec-Fetch values were paired with the wrong header names, so every
generated header set carried values invalid for the header they appear on.
Corrected to match the upstream fingerprint-suite assignment.
@zhemaituk

Copy link
Copy Markdown

This seems related as well:

if (options['http_version'] or self.options['http_version']) == '2':
            return pascalize_headers(generated)

in generator.py.

It looks like the condition is inverted, and HTTP/2 headers are pascalized, instead of intended HTTP/1.1

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.

2 participants