BrowserForge generates HTTP headers from a Bayesian traffic model
🎭 Intelligent browser header & fingerprint generator
At a glance
- What is it?
- BrowserForge is a Python library that produces browser headers matching the real frequency of browsers, operating systems and devices in the wild, using a Bayesian generative network. It suits scrapers that need plausible header sets, and it is a header generator rather than a browser automation tool.
- Who is it for?
- Adopt BrowserForge if you make plain HTTP requests with requests or a similar library and need header sets whose browser, platform and locale combination matches the real distribution rather than a fixed hardcoded string. Do not adopt it expecting a full fingerprint, since the library generates headers only, and it will not make a headless browser look like a real one.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 55 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The model generates plausible combinations, not random ones
BrowserForge is a browser header and fingerprint generator that mimics the frequency of different browsers, operating systems and devices found in the wild. The README frames it as a reimplementation of Apify's fingerprint-suite in Python, which is the clearest statement of both its scope and its lineage.
The distinguishing idea is the frequency part. A generator that picks a browser at random produces combinations that no real user has, such as a Safari user agent paired with a Windows platform hint. BrowserForge uses a Bayesian generative network to mimic actual web traffic, and the README lists the runtime as 0.1 to 0.2 milliseconds per generation, so the model can run per request rather than once per session.
The output is a plain dictionary of HTTP headers, which makes it drop into any HTTP client. The README's example output shows the full modern set: sec-ch-ua client hints, sec-ch-ua-mobile, sec-ch-ua-platform, Upgrade-Insecure-Requests, a Chrome User-Agent, Accept, the Sec-Fetch family, Accept-Encoding, and an Accept-Language with weighted values.
The claimed benefits are speed, ease of use, extensive customization across browsers, operating systems, devices, locales and HTTP version, and type safety in the implementation. The package is on PyPI as browserforge, supports Python 3.8 through 3.12 according to the README badge, and the project description calls it an intelligent browser header and fingerprint generator.
Constraints come as single values, tuples of choices, or version ranges
The generator is configured in the constructor, and the flexibility of the constraint model is the part worth understanding before you use it.
The simplest form pins everything:
headers = HeaderGenerator(
browser='chrome',
os='windows',
device='desktop',
locale='en-US',
http_version=2
)Passing tuples instead selects from a set, and the README says the options are selected based on their actual frequency in the wild:
headers = HeaderGenerator(
browser=('chrome', 'firefox', 'safari', 'edge'),
os=('windows', 'macos', 'linux', 'android', 'ios'),
device=('desktop', 'mobile'),
locale=('en-US', 'en', 'de'),
http_version=2
)For finer control you can pass Browser specification objects with version ranges and an HTTP version:
from browserforge.headers import Browser
browsers = [
Browser(name='chrome', min_version=140, max_version=145),
Browser(name='firefox', min_version=144),
Browser(name='edge', max_version=140, http_version=1),
]
headers = HeaderGenerator(browser=browsers)Two parameters deserve attention. locale takes a list of at most 10 languages for the Accept-Language header, defaulting to en-US. And strict defaults to False, meaning the generator will not raise when it cannot satisfy the constraints; with strict set to True it throws an error instead. For a scraper that needs every request to carry a coherent browser version, strict mode is the setting that turns a silent fallback into a visible failure.
generate() takes overrides, request-dependent headers and a user agent
The constructor sets the policy and generate() applies it, and the two are not identical in what they accept.
The HeaderGenerator constructor takes browser, os, device, locale, http_version and strict. The generate method takes a wider set: browser, os, device, locale, http_version, user_agent, request_dependent_headers and strict.
Two of those are worth spelling out. The user_agent parameter lets you supply a specific User-Agent to build headers around, which is how you keep a session's identity stable across requests while still generating the surrounding headers. The request_dependent_headers parameter takes known values of request-dependent headers, which is the hook for headers that depend on the specific request rather than on the client profile.
The distinction between the constructor and generate() matters for one common pattern: constructing once and calling generate() per request is the cheap path, since the model is loaded a single time. The README's 0.1 to 0.2 millisecond figure is the per-call cost in that setup, and a HeaderGenerator created inside a request loop would pay the setup cost each time instead.
The generated dictionary can be assigned straight to a requests session:
import requests
session = requests.Session()
# Set the session headers
session.headers = headers.generate()The README notes this works for requests or similar libraries by assigning to the headers attribute.
Apache-2.0, a small dependency set, and a data package doing the heavy lifting
The licence is Apache-2.0, with the LICENSE file in the repository as the authoritative text, and this is a licensing statement rather than legal advice.
The pyproject.toml shows a compact dependency list. The build backend is poetry-core, the Python constraint is ^3.8, and the runtime dependencies are click, typing_extensions for Python below 3.10, and apify_fingerprint_datapoints, which is the data package the Bayesian model consumes. orjson is an optional extra, and the extras group called all contains it, which is why the README suggests installing browserforge[all].
That dependency line is the most informative thing in the file. The probability model is not retrained in your process; it ships as data, and the library is the inference engine plus the constraint logic around it. The trade-off is that the quality of the output is bounded by how current the data package is, and the version of apify_fingerprint_datapoints you resolve becomes a meaningful part of your dependency management.
The package metadata also names the keywords, client, headers, fingerprint, generator, browser, http, scraping, requests and playwright, and the classifiers place it under Internet :: WWW/HTTP and WWW/HTTP :: Browsers. The version is 1.2.4 in pyproject.toml.
There are no GitHub releases for the repository, and the last push was on 2026-08-29, so the PyPI version and the repository state are two different things to reconcile when you check a dependency.
The difference from a browser automation tool is the whole point
The realistic comparison is with a headless browser, and it is important to be clear that BrowserForge is not that.
A browser automation tool such as Playwright launches a real browser engine, so every header, every JavaScript-visible property and every TLS fingerprint comes from that engine. It is heavy, it needs a browser binary, and it executes the page. It is also the only approach that produces a fingerprint that is internally consistent, because a real browser is the source of the values.
BrowserForge produces a header dictionary and nothing else. There is no JavaScript execution, no canvas value, no WebGL renderer string, no TLS handshake characteristic. The topics attached to the repository list both playwright and requests, so the intended combination is a plain HTTP client with generated headers, and the realistic use is for requests to sites that check headers but not much else.
The other comparison is a hardcoded header string, which is what most scrapers start with. A constant Chrome user agent sent from every request is trivially fingerprintable, because a single value across an entire traffic pattern is itself a signal. BrowserForge's contribution is that each session gets a header set drawn from the observed distribution, and the per-request cost is low enough to vary it.
The upstream project it reimplements, Apify's fingerprint-suite, is the reference for the model and the data. The difference in approach is packaging: the Python port puts the model directly in application code, which is why the installation is a plain pip install and the output is a dictionary.
Editorial conclusion
Adopt BrowserForge if you make plain HTTP requests with requests or a similar library and need header sets whose browser, platform and locale combination matches the real distribution rather than a fixed hardcoded string. Do not adopt it expecting a full fingerprint, since the library generates headers only, and it will not make a headless browser look like a real one. Before you install, check your Python version against the supported range, decide whether you need the orjson extra, and read the parameters of HeaderGenerator so that your constraints, especially browser version ranges, do not contradict themselves.
Frequently asked questions
How do I install BrowserForge and generate a set of headers?
Install it with pip install browserforge[all]. Then create a HeaderGenerator and call generate(), for example from browserforge.headers import HeaderGenerator, headers = HeaderGenerator(), then headers.generate() returns the header dictionary.
How do I constrain BrowserForge to a specific browser and operating system?
Pass the optional strings to the HeaderGenerator constructor, for example HeaderGenerator(browser='chrome', os='windows', device='desktop', locale='en-US', http_version=2). Passing tuples of several values selects from them based on their actual frequency in the wild.
What does the strict parameter do in BrowserForge?
The strict parameter defaults to False, which means the generator will not raise an error when it cannot generate headers based on the input. Setting it to True makes it throw an error in that case, which is useful when a silently substituted browser would break your expectations.
Can BrowserForge produce a full browser fingerprint or run a browser?
No. BrowserForge generates HTTP headers only, with the README describing it as a browser header and fingerprint generator. It does not execute JavaScript or launch a browser engine, so a real headless browser produces a more complete fingerprint.
What does BrowserForge depend on for its probability data?
The pyproject.toml lists apify_fingerprint_datapoints as a runtime dependency, and the README describes the project as a reimplementation of Apify's fingerprint-suite in Python. The model is a Bayesian generative network trained on data shipped in that package rather than being trained locally.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/daijro-browserforge)Was this useful?