Documentation — Internals
HTTP-Basma's algorithm's core idea centers on sending 8 specially crafted HTTP requests with different requirements to solicit different responses from the server. Once the server response is retrieved, the HTTP status line is surgically dissected for all elements and encoded optimally. Additionally, select headers from the server response are checked for encoding as well.
The terms HTTP verb and method are used synonymously throughout this document.
HTTP Requests / Probes
Setting the Request-Line. The default setup only includes the headers specified in each probe — the corresponding HTTP server responses are documented in the HTTP Responses section. All requests are sent independently of each other, without reusing the same socket; the DNS cache is cleared between every request.
GET Normal — Valid Request
The purpose of this standard-conformant request is to generate a ground-truth hash against which all other requests will be evaluated.
GET / HTTP/1.1 Host: <example.com>
GET Invalid HTTP Version Request
This request inexplicably specifies the non-standard HTTP version "4.2," seemingly just to be different from a defined protocol, in an attempt to gauge the server response for any differences from the first request.
GET / HTTP/4.2 Host: <example.com>
GET Random Resource Request
This request attempts to access a non-existent resource on the server by sending a pseudo-randomly generated target resource matching the character class [A-Za-z0-9]{16}. Servers usually respond differently when attempting to request a non-existent resource.
GET /<wAs49YmHZ0PSFKGo> HTTP/1.1 Host: <example.com>
Random Verb Request
This request employs a pseudo-randomly generated 10-character uppercase verb (matching [A-Z]{10}) to observe how the server handles and responds to an unrecognized HTTP method.
<MBBVEIIEXE> / HTTP/1.1 Host: <example.com>
get Lowercase Verb
This request uses the HTTP GET verb in all lowercase ("get"). While technically a valid verb, many servers are configured only to accept it uppercase ("GET") and will likely reject it with a unique status-line and headers.
get / HTTP/1.1 Host: <example.com>
GET Request — Accept-Encoding, Full
Sends a list of compression algorithms the sender understands, in this specific order: aes128gcm, br, <grease_A>, compress, deflate, exi, <grease_B>, gzip, pack200-gzip, x-compress, x-gzip, zstd, identity. The placeholders <grease_A> ([a-z]{8}) and <grease_B> ([a-z0-9]{10}) are pseudo-randomly generated — the server should never pick either grease value.
GET / HTTP/1.1 Host: <example.com> Accept-Encoding: aes128gcm, br, <grease_A>, compress, deflate, exi, <grease_B>, gzip, pack200-gzip, x-compress, x-gzip, zstd, identity
GET Request — Accept-Encoding, Less
Sends a reduced list of compression algorithms: <grease_A>, compress, deflate, <grease_B>, x-compress, zstd, br. Since gzip is the most likely to be picked up by the server, omitting it here forces the server to behave differently compared to P6.
GET / HTTP/1.1 Host: <example.com> Accept-Encoding: <grease_A>, compress, deflate, <grease_B>, x-compress, zstd, br
OPTIONS Request
Queries the server for the allowed HTTP methods for a given resource.
OPTIONS / HTTP/1.1 Host: <example.com>
HTTP Responses
Following each request, the server's response is analyzed to extract specific headers and their values. This extracted data undergoes further processing — dissection and encoding — to generate a reversible fingerprint. If a request fails, whether due to no server response or any other reason, the resulting fingerprint for that probe is a sequence of all zeros.
Status-Line Fingerprint 4 bytes
For P1–P5 and P8 probes, the HTTP Status-Line (a.k.a. the Start-Line) attributes are parsed and encoded. Given a response like:
HTTP/1.1 200 OK
the Status-Line consists of three octet sequences separated by a space, ending with CRLF:
<HTTP-Version><SP><Status-Code><SP><Reason-Phrase>\r\n
The Reason-Phrase is optional, per the grammar *( HTAB / SP / VCHAR / obs-text ).
HTTP-Version Fingerprint 1 byte
The HTTP-Version is encoded as two hex characters: the first encodes the protocol-name casing (HTTP=1, http=2, Http=3, other casing=4, missing/malformed=0), and the second encodes the version number (0.8=1, 0.9=2, 1.0=3, 1.1=4, ≥2.0=7, 1.>1=8, 0.<8=9, missing/malformed=0).
Status-Code Fingerprint 1 byte
- Per RFC, the Status-Code is a 3-digit code.
- It is checked against a curated list of 102 known/standardized status codes; if found, the fingerprint is the list index + 1, in hexadecimal. Otherwise it is set to
ff. - If the Status-Code is missing or not 3 digits, the fingerprint is
00. - A status code of
302in a line likeHTTP/1.1 302.1is treated as valid — only the leading302is matched. This is a deliberate design decision.
HTTP-Reason Fingerprint 2 bytes
Handling the HTTP-Reason requires a nuanced approach with specific design decisions that may intentionally deviate from strict RFC standards for practical reasons.
- The algorithm first successfully matches HTTP-Version and Status-Code, then uses flexible parsing for the reason phrase — strict adherence to the standard status-line format would cause probing checks to fail against many real-world responses with minor discrepancies. This is exercised via:
^HTTP/\d\.\d \d{3}([^]*)(case-insensitive). - If the HTTP-Reason is empty, the fingerprint is
0000. - If not empty, and it contains any character outside the restricted
acharsset (obs-text/SP·VCHAR/HTAB), the fingerprint is tagged0001(Status-Code followed by SP) or0002(otherwise). - Otherwise, the HTTP-Reason is hashed with FNV-1a and the upper two bytes of the hash are taken as the fingerprint.
FNV-1a was chosen for its simplicity, speed, small hash size, and collision resistance. Scans of the top 1 million Majestic websites found no collisions from truncating to the upper two bytes, and no collisions between real hash values and the reserved tags 0001/0002.
P3 / P4 Probes — Reason-Phrase Exception
Some servers echo the generated resource string (P3) or the rejected verb (P4) back into the Reason-Phrase to signal absence/prohibition. In these cases the echoed string is stripped from the Reason-Phrase before the FNV-1a hash is applied.
P3 examples — ~451 of the top-million Majestic servers exhibit this behaviour (generated resource string underlined in the original data):
| Domain | HTTP Response — Status-Line |
|---|---|
| 800pharm.com | HTTP/1.1 404 Page not found: /MAAdqL9vaPTTAmbP |
| accessmore.com | HTTP/1.1 404 /hReEdqHe1qUTO9J5 |
| altruja.de | HTTP/1.1 404 Event not found with slug: v3JfOERNPFmd9dPm |
| billedbladet.dk | HTTP/1.1 404 Not Found "/O3N6TJCym8xbd3h6" |
| bilpriser.dk | HTTP/1.1 400 Invalid path /Z9pvfZYXzJBxtDf3 was requested |
| dostoyanieplaneti.ru | HTTP/1.1 404 Component Not Found (https://dostoyanieplaneti.ru/yA6Hf6Q9VL427nOO) |
| eltonjohn.com | HTTP/1.1 404 The page you are looking for may not exist or may have moved: ZZL4EeviqSCqiXIg |
| pilkanozna.pl | HTTP/1.1 404 Nie znaleziono komponentu (http://pilkanozna.pl/2mc5yTBgrefQjPxp) |
| timeform.com | HTTP/1.1 404 Couldn't find a subheader with url segments hMMsHWKC90K74VHE and . |
P4 examples — ~163 of the top-million Majestic servers exhibit this behaviour (generated verb underlined in the original data):
| Domain | HTTP Response — Status-Line |
|---|---|
| vetrf.ru | HTTP/1.1 501 Method BRRKZNQVXV is not defined in RFC 2068 and is not supported by the Servlet API |
| viafrance.com | HTTP/1.1 501 Unsupported method ('VWYEQKTUPY') |
| viapresse.com | HTTP/1.1 405 HTTP method 'ZWHAXAYFXN' is not allowed ! |
| walkabout.com.au | HTTP/1.1 405 Method not allowed: IDGXOBDKYA |
| warzone.com | HTTP/1.1 501 Method JTZTIUDBKI is not defined in RFC 2068 and is not supported by the Servlet API |
| cibersur.com | HTTP/1.1 501 El Metodo TTTXYVEHBS no esta definido en la especificacion RFC 2068 y no es soportado por la API Servlet |
| dancenter.de | HTTP/1.1 405 Method ZQPEXTSQAU not implemented. |
| opony.com.pl | HTTP/1.1 405 Request method 'LQTMOQKIRL' not supported |
| una.fr | HTTP/1.1 501 Le mode VEHCPLTMUN n'est pas défini dans la RFC 2068 et n'est pas supporté par l'API Servlet |
Content-Length & Transfer-Encoding Fingerprint (C-L_T-E) 1 byte
With probes P2–P5 and P8, the algorithm looks for the Content-Length header first; if absent, it falls back to Transfer-Encoding. Computation depends on the Status-Line first passing validation.
- If the Status-Line fails validation, the fingerprint is
99. - If neither header exists: fingerprint is
00if the body is empty, otherwise1+encode_length(size), where length encodes as0(=0),1(=1), or2(>1). - If Content-Length exists: fingerprint =
encode_name(cl_name) | encode_length(value), where the name-casing digit is2=Content-Length,3=content-length,4=Content-length,5=content-Length,6=any other casing that lowercases to content-length. - If Transfer-Encoding with value "chunked" exists: fingerprint =
encode_name(te_name) | encode_length(body size), where the name-casing digit is7=Transfer-Encoding,8=transfer-encoding,9=Transfer-encoding,a=transfer-Encoding,b=any other casing.
Canonical "Content-Length" casing (2) + length >1 (2).
Canonical "Transfer-Encoding" casing (7) + body length >1 (2).
Strict-Transport-Security (HSTS) Header Fingerprint 1 byte
For probe P1, the HSTS header is fingerprinted in detail. It signals that a site must only be accessed over HTTPS, e.g. Strict-Transport-Security: max-age=31536000; includeSubDomains; preload. If the Status-Line is invalid the fingerprint is 99; if the header is absent, it is 00. Otherwise, only the first 7 bits are used, bit-encoding these attributes (all occurrences accounted for):
| Bit | Meaning |
|---|---|
| 0 | max-age attribute exists |
| 1 | max-age == 0 (matches ^max-age=0+$) |
| 2 | max-age value is empty |
| 3 | includeSubDomains exists |
| 4 | preload exists |
| 5 | more than three fields/attributes present (catches duplicates/extras) |
| 6 | at least one empty attribute exists (e.g. max-age=25;;) |
If the bitset is all zero (HSTS present with no attributes) the fingerprint is set to ff.
Content-Encoding (CE) Header Fingerprint 1 byte
For probes P6 and P7, the Content-Encoding response header is fingerprinted against the compression algorithms offered in Accept-Encoding (the 13-item A-E_List: aes128gcm, br, <grease_1>, compress, deflate, exi, <grease_2>, gzip, pack200-gzip, x-compress, x-gzip, zstd, identity). Invalid Status-Line → 99; header absent → 00. All occurrences are accounted for. Bit layout:
| Bits | Meaning |
|---|---|
| 0–3 | index of the value in A-E_List + 1, or 0x0f if not in the list |
| 4 | value contains an empty sequence (e.g. Content-Encoding: ,) |
| 5–7 | count of additional compression algorithms returned (capped at 7; forced to 7 if it would otherwise collide with fingerprint 99) |
Allow, Access-Control-Allow-Methods & Public Headers Fingerprint (AAP) 3 bytes
In probe P7, the response is examined for Allow, Access-Control-Allow-Methods (ACAM), and Public headers (collectively AACAMP). Allowed verbs across every occurrence of these headers are collected, bit-encoded, normalized, and FNV-1a hashed — producing a 3-byte fingerprint: AAP_FP = <1-byte bitset+count><2-byte FNV-1a hash>. Invalid Status-Line → 999999.
Access-Control-Allow-Origin: * Access-Control-Allow-Methods: GET, POST, OPTIONS Allow: OPTIONS,GET,HEAD,Trace Allow: POST Public: POST, TRACE
- Collect & sort verbs per header type: Allow = {GET, HEAD, OPTIONS, POST, Trace}; ACAM = {GET, OPTIONS, POST}; Public = {POST, TRACE}.
- Concatenate in order Allow → ACAM → Public: GETHEADOPTIONSPOSTTraceGETOPTIONSPOSTPOSTTRACE
- FNV-1a hash, take upper two bytes: 0xd02f8e3f → d02f (or
0000if no verbs exist).
The 1-byte bitset+count prefix: bit 0 = Allow header exists, bit 1 = ACAM exists, bit 2 = Public exists, bits 3–7 = total verb count across all headers (capped at 31). For the example above this is 01001111 = 4f.
Connection Header Fingerprint (Cnx_FP) 2 bytes
For probes P2, P3, P4, P5 and P7, a 2-byte bit-encoded fingerprint tracks whether the Connection header value is Keep-Alive or Close (checked case-insensitively). Every occurrence is registered, even where a server sends the header more than once with conflicting values (e.g. Connection: keep-alive, close or a repeated header). Format: Cnx_FP = <1-byte keep-alive bitset><1-byte close bitset> — each byte uses its first 5 bits, one per probe.
| Bit | Probe | Keep-Alive |
|---|---|---|
| 0 | P2 | Set/Unset |
| 1 | P3 | Set/Unset |
| 2 | P4 | Set/Unset |
| 3 | P5 | Set/Unset |
| 4 | P7 | Set/Unset |
| Bit | Probe | Close |
|---|---|---|
| 0 | P2 | Set/Unset |
| 1 | P3 | Set/Unset |
| 2 | P4 | Set/Unset |
| 3 | P5 | Set/Unset |
| 4 | P7 | Set/Unset |
Verbosus Fingerprint 38 bytes
The verbosus fingerprint is a 38-byte (76-character) string: 01[0-9A-Fa-f]{74}. The initial byte, "01," indicates the hash version, while the subsequent 37 bytes contain the combined fingerprints from all probes (P1–P8) and the Connection fingerprint (Cnx_FP). Version "02" is reserved for the Pacto fingerprint and cannot be reused.
| Probe | Len | Fingerprint components (concatenated) |
|---|---|---|
| P1 — GET Normal, Valid Request | 5 | Status-Line (4) | Strict-Transport-Security (1) |
| P2 — GET Invalid HTTP Version | 5 | Status-Line (4) | Content-Length (1) |
| P3 — GET Random Resource | 5 | Status-Line (4) | Content-Length (1) |
| P4 — Random Verb | 5 | Status-Line (4) | Content-Length (1) |
| P5 — get Lowercase Verb | 5 | Status-Line (4) | Content-Length (1) |
| P6 — Accept-Encoding (Full) | 1 | Content-Encoding |
| P7 — Accept-Encoding (Less) | 1 | Content-Encoding |
| P8 — OPTIONS Request | 8 | Status-Line (4) | Content-Length (1) | Allow/ACAM/Public (3) |
| Cnx_FP (P2,P3,P4,P5,P7) | 2 | Connection: Keep-Alive (1) | Connection: Close (1) |
| P1|…|P8|CONX | 37 | — |
Using the HTTP-Basma tool against example.com:
HTTPBasma.exe --domain example.com --port 80
Dissected:
P1: 140a85e400 P2: 1420958a22 P3: 142494d622 P4: 1320958a22 P5: 1420958a22 P6: 08 P7: 00 P8: 144d9e7f22000000 Cn: 1b04
The tool can emit CSV/JSON with the full dissection: fingerprint (p1_fp…), status line (p1_sl…), and raw response headers (p1_rh…) per probe, plus an experimental response-header fingerprint per probe (p1_rh_fp…, see Tool). In CSV/JSON output, P6/P7/P8 are represented as p6f_fp, p6l_fp, and p7a_fp respectively.
Pacto Fingerprint 32 bytes
For example.com, the Pacto fingerprint is:
The leading 02 marks it as a Pacto fingerprint. The remaining 15 bytes are the first 15 bytes of the SHA-256 hash of the verbosus fingerprint excluding its version byte — i.e. SHA-256 of 140a85e4001420958a22142494d6221320958a221420958a220800144d9e7f220000001b04, truncated to 15 bytes, then prefixed with 02.
If verbosus_fp is all zeros, pacto_fp is likewise set to all zeros.
Tool — HTTP-Basma
HTTP-Basma is a C++ tool developed to showcase the practicality and viability of this algorithm. It leverages Chilkat's library for all HTTP socket interactions and other supporting classes. The tool includes a demangler feature that can dissect and reverse the verbosus fuzzy-hash into a comprehensive JSON object.
Some tool output uses slightly different probe labels, but the underlying order is consistent: P1→P1, P2→P2, P3→P3, P4→P4, P5→P5, P6→P6F, P7→P6L, P8→P7a.
When requesting a given domain/IP, results can be saved to a CSV or JSON file with detailed information about server response headers and each probe's fingerprint. The tool supports HTTP redirects (enabled by default).
Experimental response-header fingerprint
An experimental algorithm fingerprints the full set of response headers (for comparison purposes), used for the p1_rh_fp…p8_rh_fp columns:
- Collect all response headers except
Connection: Keep-Alive,Connection: Close, and any header starting withX-(checked case-insensitively). - Save all unique header names into a vector.
- Sort the vector alphabetically.
- Concatenate all sorted header names into one string.
- FNV-1a hash the string and take the full 32-bit hash as the fingerprint.
- If no headers are present, the fingerprint is zero.
Date: Wed, 19 Mar 2025 15:02:53 GMT Content-Type: text/html;charset=UTF-8 Cache-Control: max-age=1 Connection: keep-alive X-Powered-By: ASP.net Content-Length: 0 Sever: Microsoft-IIS/10.0
Sorted, concatenated string: "Cache-ControlContent-LengthContent-TypeDateSever" → FNV-1a → df0062c6
The Demangler
The tool's demangler (-i / --demangle_json) takes a verbosus fingerprint and reconstructs the attributes of each probe as a JSON object. To reverse FNV-1a hashes back into human-readable values, it consults two local lookup databases — options.csv (allowed HTTP methods) and status_line_db.csv (status-line reason phrases) — both compiled from a scan of the top 1 million Majestic websites. If either file is missing, the corresponding hash-reversal feature is automatically disabled.
Demangling the verbosus fingerprint for example.com:
"sl_reversed_db": {
"http_version": "HTTP/1.1",
"status_code": [200, 404, 403, 500, 204, 999, 888, 603],
"http_reason": "OK"
}
The status_code array holds multiple codes because different servers reuse the same reason phrase for different status codes, producing identical FNV-1a hashes.
For domain 0009.in — verbosus 01140a85e400145183b23200000000001420958a321420958a320202140a85e430216e12100d — the Allow-header data demangles to:
"allow_hdr": {
"fp": "216e12",
"hdr": {
"fp": "21",
"methods": {
"total": 4,
"total_cmt": "total number of allowed methods across all allow headers' types",
"hash": "6e12"
},
"hdrs": ["allow"]
},
"hdrs_value": {
"allow": ["OPTIONS,HEAD,GET,POST"]
}
}
The allow array's methods (OPTIONS,HEAD,GET,POST) correspond to FNV-1a hash 6e12, retrieved from options.csv.
The Comparator Option
The comparator (-C / --compare) compares two verbosus fingerprints and prints the differences across each probe's major components.
HTTPBasma.exe --compare 01140a85e40014514bd522142494d622144d9e7f221420958a220800140a85e4202931de1609,01140a85e40014514bd522142494d622144d9e7f221420958a220800140a85e42029e4a01609
< FPrnt-1 Vs. FPrnt-2 >
[ P1 ] [ P2 ] [ P3 ] [ P4 ] [ P5 ] [ P6F ] [ P6L ] [ P7a ] {Allow Header(s) Hash}
allow header: 31de != e4a0
The diff isolates a mismatch in the hash component of the P7a (Allow-header) probe.
Scanning a List of Domains
For batch scanning of domains, IPs, or URLs, supply the list via -f / --file and choose an output format with -c/--csv or -j/--json (one is required). Scanning runs sequentially by default; add -P/--parallel to parallelize.
- Each domain must be on its own line; lines starting with
;,//, or#are skipped. -p/--portor-s/--sslapply the same port/SSL setting to every domain in the file.- Port and SSL can instead be inferred per-domain from its URL: an
httpsprefix sets SSL and port 443; the default port is 80 otherwise; a trailing colon sets an explicit port (e.g.https://www.nohereorthere.com:8083→ SSL on, port 8083). The same applies to-d/--domain.
Experimentation
For experimentation, we scanned the top 1 million Majestic websites (986,910 reachable sites) as well as several malware C&C servers and frameworks, from February 2024 to late March 2024 — server fingerprints may have since changed.
Observations
- Amazon.com and similar servers return varying response-header sets across identical requests — likely CDN/load-balancer intermediaries injecting request-specific headers (e.g. a Cloudflare cache miss adds
X-Cache: Miss from cloudfront). Standalone servers keep a consistent header set. - Header-name casing can change across requests — observed for
connection,server,content-type, anddate, sometimes returned fully lowercase. - The presence of the Connection header is inconsistent across repeated queries to the same server.
- Per spec, a 405 Method Not Allowed response should include an Allow header listing supported methods — thousands of servers fail to do so.
C&C RAT Frameworks Fingerprints
| Framework | Verbosus | Pacto | FP rate |
|---|---|---|---|
| CobaltStrikeA | 011420958a0014514bd5221420958a221420958a221420958a2200001420958a22000000001f | 02464ae8b7d86f82c9918e2c2b9d6b91 | 72 / 986,910 |
| QakBotB | 01140a85e400140a85e4220000000000000000000000000000000000140a85e4220000000000 | 02d8b85b0eb91688f2d989ba28dc40b9 | 8 / 986,910 |
| VShellC | 01140a85e400140a85e422000000000000000000000000000000000000000000000000000000 | 028a00645c26f58fd00470d0350361f2 | 97 / 986,910 |
| HavocD | 01142494d60914514bd522142494d6221420958a701420958a220000140e04922032c37f1609 | 020769322f3d94ac2f258ddf5ce08502 | 0 |
| BruteRatelE | 01140a85e40014512f3612140a85e422140a85e422140a85e4220000140a85e4220000000001 | 0207292309a7a7e798e417d69df5f2a5 | 23 / 986,910 |
Top 1 Million Majestic Websites
All requests sent over port 80, no SSL, follow-redirect enabled.
| Property | Verbosus | Pacto |
|---|---|---|
| YouTube 37 TLDs | 01140a85e4011320958a22142494d67214254c5e2214254c5e22080014254c5e220000000000 | 02cc5be6d05192e17de041538508bc22 |
| Google 231 TLDs + Maps & Video | 01140a85e4001320958a22142494d62214254c5e2214254c5e22080014254c5e2211696f0000 | 020867968f811db2a53640846e56e28e |
| Facebook 102 TLDs across Facebook, Instagram, Oculus, WhatsApp, Messenger, Meta | 01140a85e411140a85e412140a85e4721420958a221420958a220c0c140a85e472000000120d | 02487e07a6c0f178da22b974a4965dd3 |
| Twitter / X 11 services | 01140a85e401130a85e412140a85e4821420958a321420958a320805140a85e482000000000d | 02c9ff1f429859e3fafd20096bbe2deb |
| Yahoo 18 TLDs + services | 01140a85e40114515c9322142494d672140a85e422140a85e4220808140a85e4220000001e00 | 02d3208e7deed3237ccdf577d5a8103d |
| Pornhub 20 TLDs + services | 010000000000000000000000000000001425d6cf321420958a3200001425d6cf320000000000 | 025ea95c23041d1df40bcc2bc867d812 |
| Amazon Least reliable of them all — varies across requests (e.g. one server uses Transfer-Encoding, another Content-Length) | v1: 01140a85e41914514bd522142494d6721420958a221420958a2208001420958a20000000120d v2: 01140a85e40014514bd522142494d6721420958a221420958a2208001423a9a822000000120d | 02738a61e52960b24ed77a47aa05cc19 (both) |
Top 10 Fingerprints (Top 1-Million Majestic Websites)
| # | Verbosus | Pacto | Count |
|---|---|---|---|
| 1 | 01140a85e40014514bd522142494d6221425d6cf221420958a2208001425d6cf220000001609 | 0212b0b256d2ec384e0d16859573c740 | 59,085 |
| 2 | 01140a85e40014514bd522142494d67214254c5e721420958a22080014254c5e720000001609 | 0212b0b256d2ec384e0d16859573c740 | 42,385 |
| 3 | 01140a85e40014514bd522142494d672140a85e4721420958a220202140a85e4720000001609 | 0201ff6c288831a114f0cdb5f9ef46a3 | 28,991 |
| 4 | 011423a9a80014514bd5221423a9a8221425d6cf221420958a2208001423a9a8220000001609 | 02fe0e8205642eb07bdc8f83b028c803 | 18,281 |
| 5 | 01000000000014514bd522000000000000000000001420958a22000000000000000000000009 | 02e19199d985ef687795639662f5e7cc | 14,974 |
| 6 | 01140a85e40014514bd522142494d672140a85e4721420958a220800140a85e4720000001609 | 02211726ab32f439fcad5b8e55a4ed31 | 11,813 |
| 7 | 01140a85e400140a85e472142494d672140a85e472140a85e4720800140a85e4720000000000 | 02814f248da80d4220da551fbe68e0c7 | 11,040 |
| 8 | 011423a9a80014514bd5221423a9a8221423a9a8221420958a2200001423a9a8220000001609 | 022daa4e2c505ae4a6ee04f3a8c98c86 | 9,957 |
| 9 | 01140a85e40014514bd522142494d67214254c5e721420958a22020214254c5e720000001609 | 0246d98c10269e02096cdc1024b12d4a | 8,345 |
| 10 | 011423a9a80014514bd5221423a9a8221423a9a8221420958a2208001423a9a822000000001f | 02ebabc0dbf3212783eb5109e94518ec | 5,846 |
Some Interesting Content-Encoding Values
Values seen across the top 1 million Majestic websites that deviate from the standard, highlighting a lack of uniform adherence.
Some Interesting Options Allow Header Values
P8 (OPTIONS) elicited unusual Allow-header values that either don't follow standard comma-separated syntax or are rarely seen — not all servers adhere to the specification. Of particular interest: servers responding with an empty Allow header, signalling the resource currently accepts no HTTP methods.
| Allow header value |
|---|
| * |
| GET;HEAD;OPTIONS |
| GET, GET |
| OPTIONS, GET, HEAD, POST |
| HEAD,HEAD,GET,HEAD,POST,OPTIONS,TRACE |
| CLI, GET, POST |
| HEAD;HEAD;HEAD;GET |
| GET|POST|HEAD |
| ACL,BIND,CHECKOUT,CONNECT,COPY,DELETE,GET,HEAD,LINK,LOCK,M-SEARCH,MERGE,MKACTIVITY,MKCALENDAR,MKCOL,MOVE,NOTIFY,PATCH,POST,PROPFIND,PROPPATCH,PURGE,PUT,REBIND,REPORT,SEARCH,SUBSCRIBE,TRACE,UNBIND,UNLINK,UNLOCK,UNSUBSCRIBE |
| CHECKOUT,CONNECT,COPY,DELETE,GET,HEAD,LOCK,M-SEARCH,MERGE,MKACTIVITY,MKCOL,MOVE,NOTIFY,PATCH,POST,PROPFIND,PROPPATCH,PURGE,PUT,REPORT,SEARCH,SUBSCRIBE,TRACE,UNLOCK,UNSUBSCRIBE |
| OPTIONS, GET, HEAD, TRACE, PROPFIND, PROPPATCH, MKCOL, COPY, PUT, DELETE, MOVE, LOCK, UNLOCK, BIND, REBIND, UNBIND, VERSION-CONTROL |
| ,HEAD,POST,GET,HEAD,OPTIONS |
| *, * |
| POST,,HEAD,mщfU,GET,HEAD,OPTIONS,,HEAD,,HEAD |
| GET,HEAD;GET, POST, HEAD, OPTIONS, PUT, DELETE |
| GET,GET,GET |
| get, post, head |
| get, head, delete, options |
| get |
| get, post |
| [DELETE, POST, GET, OPTIONS, PUT, PATCH] |
| REDIRECT GET HEAD POST PUT DELETE OPTIONS SEND FORWARD FAIL |
| (empty, no value) |
| GET POST HEAD |
| GET HEAD OPTIONS |
Access-Control-Allow-Methods — interesting values
| ACAM header value |
|---|
| "PUT, GET, POST, DELETE, OPTIONS" |
| true |
| https://eu-app.contentstack.com |
| GET, POST, PATCH, PUT, DELETE, OPTIONS;GET,POST,PUT,DELETE,OPTIONS |
| GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, SCRIPT |
| GET;GET, POST, PUT, DELETE, PATCH, PURGE, HEAD, OPTIONS, FETCH;GET, POST, PUT, DELETE, PATCH, PURGE, HEAD, OPTIONS, FETCH |
| Content-Type |
| *;GET, POST, PUT, DELETE, OPTIONS |
| *;GET |
| *;* |
| 'HEAD, GET, POST, PUT, PATCH, DELETE' |
| * |
| (empty, no value) |
One notable example returned the literal valuetrueand, in a separate response, leaked an origin URL (https://eu-app.contentstack.com) directly into the header value.
Public header — interesting values
| Public header value |
|---|
| put, trace, options, delete, post, get, head |
| OPTIONS, TRACE, GET, HEAD, POST (alongside X-Frame-Options: SAMEORIGIN) |
| OPTIONS, TRACE, GET, HEAD, POST;GET,POST,HEAD |
Status-Line Reason Phrases
A sample of the distinct status-line reason phrases observed — approximately 680 unique phrases were returned in total across the servers analyzed:
Conclusion
HTTP fingerprinting a server to generate a specific impression for identification is an ad-hoc approach of trial and error. However, creating a truly effective fingerprint — one that's reversible, modular, shareable, compact, and optimized — requires sophisticated techniques like bit-encoding, hashing, and data transformation. The HTTP-Basma verbosus fingerprint format is the first algorithm to successfully achieve these objectives.
This paper showcased HTTP-Basma's practical application and feasibility by successfully grouping fingerprints into clusters based on server configurations and behaviours, validated against both the top one million Majestic websites and malicious C&C servers.
HTTP-Basma has limitations, including vulnerability to false positives and false negatives. A minor alteration — such as replacing a status-line reason phrase — can change the fingerprint for an otherwise-identical server; depending on interpretation, that evasion technique itself constitutes an identity change requiring a new fingerprint. Inconsistencies in whether a server returns the Connection header across different execution times also produce different fingerprints.
Bibliography
- Kotarak, A. (2003, May 23). WebServerFP-Source.zip. Retrieved May 14, 2026, from PACKET STORM: packetstorm.news/files/id/31157
- Lee, D. W. (2001). HMAP: A Technique and Tool For Remote Identification of HTTP Servers. University of California, Computer Science. Davis: Computer Security Lab. Retrieved May 14, 2026, from seclab.cs.ucdavis.edu
- Ruef, M. (2007, December 6). httprecon project — advanced web server fingerprinting. Retrieved May 14, 2026, from computec.ch: computec.ch/projekte/httprecon
- Shah, S. (2004, May 19). An Introduction to HTTP fingerprinting. Retrieved May 14, 2026, from Net Square: net-square.com/httprint_paper