Checks whether a request looks like what its user agent claims to be.
Browsers send a predictable set of headers. Automation that borrows a
browser's user agent string rarely reproduces all of them, and never
reproduces them consistently. This signal classifies the user agent (see
Limen.Signal.UserAgent) and reports inconsistencies as flags:
| Flag | Meaning |
|---|---|
:no_user_agent | no user-agent header |
:no_accept | no accept header |
:no_accept_language | no accept-language header |
:no_accept_encoding | no accept-encoding header |
:generic_accept | a browser navigation with accept: */* |
:no_sec_fetch | a browser recent enough to send sec-fetch-* headers did not |
:no_client_hints | a Chromium browser recent enough to send sec-ch-ua did not |
:unexpected_client_hints | a non-Chromium browser sent sec-ch-ua |
:client_hint_brand_mismatch | sec-ch-ua does not name the claimed browser |
:client_hint_platform_mismatch | sec-ch-ua-platform disagrees with the user agent |
:client_hint_mobile_mismatch | sec-ch-ua-mobile disagrees with the user agent |
:headless | the user agent or client hints name a headless browser |
Browsers only send sec-fetch-* headers (what a request
is for, such as a page or an image) and client hints
(sec-ch-ua*, in which Chromium browsers describe themselves) to secure
origins, so those checks only apply to HTTPS requests. Behind a TLS
terminator, make sure conn.scheme reflects the original scheme (for
example with Plug.RewriteOn) before Limen.Plug runs.
This is an independent design, not JA4H (FoxIO's HTTP fingerprint).
Shape
:shape is a stable hash of the request's header names in the order
they were received, leaving out headers added by proxies. Clients built on
the same HTTP stack share a shape, so it works well in lists and rate keys.
Header order is only meaningful when the web server preserves it: Bandit
does, Cowboy does not.
Provides :ua_family (see Limen.Signal.UserAgent for the families),
:ua_name (the name of a known automated client, such as "googlebot",
"gptbot" or "curl", else nil), :ua_version, :shape and
:shape_flags, with the parsed user agent as evidence for :ua_family.
Summary
Functions
The inconsistencies between a request's headers and its user agent.
The header-order shape of a request.
Functions
@spec flags(Limen.Context.t(), Limen.Signal.UserAgent.t()) :: [atom()]
The inconsistencies between a request's headers and its user agent.
@spec shape(Limen.Context.t()) :: String.t()
The header-order shape of a request.