User-Agents and Browser Fingerprinting
The default requests User-Agent (python-requests/2.x) is the fastest possible way to get blocked. But modern
anti-bot stacks look at far more than one header.
The full fingerprint, roughly in order of what sites actually check:
- User-Agent string — easiest to fix
- Accept / Accept-Language / Accept-Encoding — browsers send these;
requestsminimally does - Header order — real browsers send headers in a specific order that
requestsdoesn’t replicate by default - TLS fingerprint (JA3/JA4) — the cipher suite and extension order your TLS client sends.
requestslooks nothing like Chrome at this layer. - HTTP/2 fingerprint — frame order, window sizes, headers
- Behavioral signals — mouse movement, scroll, timing
You can fix 1 and 2 with plain requests. For 3–5, you need a library that mimics a browser’s network stack:
# curl_cffi impersonates Chrome's TLS and HTTP/2 fingerprint
from curl_cffi import requests
resp = requests.get(url, impersonate="chrome124")
For level 6, you need an actual browser — Playwright or Selenium — often with a stealth plugin that patches
navigator.webdriver and similar tells.
Rule of thumb: start minimal. If curl_cffi with impersonate="chrome124" and a sane Accept-Language gets past a
site, don’t over-engineer. Real browsers are expensive; only pay that cost when you have to.