Se gestisci infrastrutture di scraping o automazione browserless, hai probabilmente notato che i sistemi anti-bot più avanzati — Akamai Bot Manager, Cloudflare, DataDome — non si limitano più a controllare il tuo indirizzo IP o il tuo fingerprint TLS. Dal 2024, questi sistemi ispezionano il fingerprinting HTTP/2 spiegato a livello di frame: i parametri SETTINGS, l'ordine degli pseudo-header, i valori WINDOW_UPDATE e la priorità degli stream. Un client Python come httpx che dichiara Chrome 148 tramite JA4 ma invia HEADER_TABLE_SIZE 4096 viene flaggato con il massimo bot score prima ancora che l'HTML venga scaricato.
Cos'è il fingerprinting HTTP/2 spiegato
Il fingerprinting HTTP/2 è la tecnica con cui un server identifica il client che si connette analizzando i frame di controllo e di intestazione del protocollo HTTP/2, prima ancora che il contenuto della risposta venga processato. A differenza del fingerprinting TLS (JA3/JA4), che opera al livello di trasporto e viene completato durante l'handshake, il fingerprinting HTTP/2 avviene dopo l'handshake TLS, nei primi millisecondi della connessione HTTP/2 stessa.
Il protocollo HTTP/2, definito nella RFC 9113, prevede che il client invii un frame SETTINGS immediatamente dopo il preface magico (PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n). Questo frame contiene parametri di configurazione che ogni implementazione imposta in modo diverso. Poiché la specifica lascia ai client ampia libertà sui valori predefiniti, ogni browser e ogni libreria HTTP produce un SETTINGS frame con valori distinti — un vero e proprio impronta digitale.
I sistemi anti-bot moderni confrontano questa impronta con un database di fingerprint noti. Se il tuo client dichiara di essere Chrome tramite l'User-Agent e il fingerprint TLS, ma il frame SETTINGS corrisponde a quello della libreria h2 di Python, il server assegna un punteggio di bot elevato. La discrepanza è immediatamente rilevabile perché i browser non inviano mai HEADER_TABLE_SIZE 4096 — il valore predefinito di nghttp2/httpx.
Il frame SETTINGS e i parametri fingerprintati
Il frame SETTINGS è il vettore principale del fingerprinting HTTP/2. I parametri che vengono ispezionati sono cinque:
- HEADER_TABLE_SIZE (0x1): la dimensione della tabella di compressione HPACK. Chrome imposta 65536 byte, Firefox 65536, mentre la libreria
h2di Python usa 4096 byte come predefinito — un valore che nessun browser mainstream utilizza. - ENABLE_PUSH (0x2): indica se il client accetta server push. Chrome imposta 0 (disabilitato) dalla versione 73+, Firefox 0, Safari 1. Un client che non invia questo parametro o lo imposta a 1 è sospetto.
- MAX_CONCURRENT_STREAMS (0x3): il numero massimo di stream concorrenti. Chrome invia 1000, Firefox 1000. La libreria
h2non invia questo parametro, lasciando il valore al default del server (di solito illimitato). - INITIAL_WINDOW_SIZE (0x4): la dimensione iniziale della finestra di controllo di flusso. Chrome imposta 6291456 byte (6 MB), Firefox 131072 byte (128 KB). Il valore predefinito di
h2è 65535 byte — diverso da entrambi i browser. - MAX_HEADER_LIST_SIZE (0x6): la dimensione massima della lista header. Chrome invia 262144 byte (256 KB).
h2non invia questo parametro.
Oltre al frame SETTINGS, vengono fingerprintati altri due segnali:
- WINDOW_UPDATE: dopo il frame SETTINGS, il client invia un frame
WINDOW_UPDATEa livello connessione per aumentare la finestra di controllo di flusso. Chrome invia un incremento di 15663105 byte, portando la finestra totale a 15728640 byte (15 MB). Questo valore è specifico di Chrome e non viene replicato da alcuna libreria HTTP standard. - Stream priority: Chrome utilizza un albero di priorità basato su dipendenze e pesi. Nei frame
HEADERSoPRIORITY, invia informazioni sulla dipendenza dello stream (stream ID 0 per la root, pesi specifici). La libreriah2non replica questa struttura.
Ordine degli pseudo-header
Il terzo segnale critico è l'ordine degli pseudo-header HTTP/2. La RFC 9113 richiede che gli pseudo-header (:method, :path, :scheme, :authority) precedano gli header regolari, ma non specifica l'ordine tra loro. Ogni browser usa un ordine diverso:
- Chrome:
:method,:authority,:scheme,:path→m,a,s,p - Firefox:
:method,:path,:scheme,:authority→m,p,s,a - Safari:
:method,:scheme,:path,:authority→m,s,p,a
La libreria h2 di Python invia gli pseudo-header nell'ordine m,s,p,a — che non corrisponde a nessun browser mainstream. Questo è un segnale inequivocabile di automazione.
L'impronta Akamai h2 e JA4H
Akamai è stato pioniere nella normalizzazione del fingerprinting HTTP/2. Il formato Akamai h2 fingerprint compatta tutti i segnali del protocollo in una singola stringa con la struttura:
SETTINGS_KEY:VALUE,SETTINGS_KEY:VALUE,...|WINDOW_UPDATE|PRIORITY|PSEUDO_HEADER_ORDER
Per esempio, il fingerprint HTTP/2 di Chrome 148 su Windows appare così:
1:65536,2:0,3:1000,4:6291456,6:262144|15663105|0|m,a,s,p
Dove 1:65536 è HEADER_TABLE_SIZE:65536, 2:0 è ENABLE_PUSH:0, e così via. Il valore 15663105 dopo il primo pipe è l'incremento del WINDOW_UPDATE a livello connessione. Lo 0 dopo il secondo pipe indica l'assenza di un albero di priorità complesso (semplificato). Infine, m,a,s,p è l'ordine degli pseudo-header.
Il fingerprint di httpx con la libreria h2 appare invece:
1:4096,4:65535|0|0|m,s,p,a
La differenza è drammatica: valori diversi, parametri mancanti (nessun MAX_CONCURRENT_STREAMS, nessun MAX_HEADER_LIST_SIZE, nessun ENABLE_PUSH), e ordine pseudo-header errato. Un sistema anti-bot non ha bisogno di ML per classificare questo client come bot — basta un confronto esatto con il database dei fingerprint noti.
JA4H: il fingerprint HTTP standardizzato
JA4H è il componente HTTP della famiglia di fingerprint JA4 sviluppata da FoxIO. Mentre JA3/JA4 catturano il fingerprint TLS, JA4H cattura il fingerprint HTTP a livello applicazione. Il formato JA4H include:
- Versione del protocollo HTTP (HTTP/1.1 vs HTTP/2 vs HTTP/3)
- Numero di header e loro nomi (ordinati)
- Lingua accettata (Accept-Language primary value)
- Presenza di cookie e referer
Un esempio di JA4H per Chrome 148:
ge11nn20enus_acch..._c001_r001
Dove ge indica HTTP/2 su TLS, 11 è il numero di header, nn indica nessun Accept-Language secondario, 20 e enus codificano la lingua, e le sezioni dopo l'underscore elencano gli header ordinati, la presenza di cookie e referer.
JA4H e l'Akamai h2 fingerprint sono complementari: JA4H cattura gli header HTTP, mentre l'Akamai fingerprint cattura i frame del protocollo HTTP/2. Un sistema anti-bot moderno li utilizza entrambi.
Perché JA4 e HTTP/2 devono concordare
Il fingerprint TLS (JA4) e il fingerprint HTTP/2 devono essere coerenti. Se il tuo JA4 corrisponde a Chrome 148 su Windows — con le estensioni SNI, ALPN h2, le curve X25519 e l'ordine dei cipher di Chrome — ma il frame HTTP/2 SETTINGS contiene HEADER_TABLE_SIZE 4096, il server rileva una contraddizione immediata.
Questo accade perché i browser producono sia il fingerprint TLS che quello HTTP/2 dalla stessa implementazione (BoringSSL + nghttp2 per Chrome, NSS + neqo per Firefox). È praticamente impossibile che un JA4 Chrome si accompagni a un SETTINGS frame non-Chrome. I sistemi anti-bot trattano questa incongruenza come prova definitiva di spoofing.
La logica di scoring è semplice:
- Il server estrae il JA4 dal ClientHello TLS.
- Il server estrae l'h2 fingerprint dai frame HTTP/2.
- Il server verifica che la coppia (JA4, h2_fingerprint) sia presente nel database delle combinazioni note.
- Se la coppia non esiste — per esempio JA4 Chrome con h2 fingerprint di httpx — il bot score viene impostato al massimo prima ancora che la richiesta HTTP venga processata.
Questo significa che non basta modificare l'User-Agent o usare una libreria TLS che imita Chrome. Devi replicare entrambi i livelli: TLS e HTTP/2.
Client Python e Node che perdono frame incoerenti
La maggior parte delle librerie HTTP di alto livello produce fingerprint HTTP/2 che non corrispondono a nessun browser. Ecco i casi più comuni:
| Client | HEADER_TABLE_SIZE | MAX_CONCURRENT_STREAMS | INITIAL_WINDOW_SIZE | Pseudo-header order | Match browser |
|---|---|---|---|---|---|
| Chrome 148 | 65536 | 1000 | 6291456 | m,a,s,p | Nativo |
| Firefox 130 | 65536 | 1000 | 131072 | m,p,s,a | Nativo |
| httpx (Python, h2) | 4096 | non inviato | 65535 | m,s,p,a | d>Nessuno|
| requests (Python) | N/A (HTTP/1.1) | N/A | N/A | N/A | Nessuno |
| Node http2 (nghttp2) | 4096 | non inviato | 65535 | m,s,p,a | Nessuno |
| got (Node.js) | 4096 | non inviato | 65535 | m,s,p,a | Nessuno |
| curl_cffi (impersonate=chrome) | 65536 | 1000 | 6291456 | m,a,s,p | Chrome |
Il pattern è chiaro: httpx, il modulo http2 di Node.js e got utilizzano tutti nghttp2 o la libreria h2 con i valori predefiniti, che non corrispondono a nessun browser. Il HEADER_TABLE_SIZE 4096 è il segnale più evidente — nessun browser mainstream lo usa.
curl_cffi si distingue perché utilizza la libreria curl-impersonate, che compila curl con BoringSSL e configura nghttp2 per replicare esattamente i valori SETTINGS dei browser specifici. Quando usi impersonate="chrome", curl_cffi produce un JA4 e un h2 fingerprint che corrispondono a Chrome.
Perché i proxy residenziali sono ancora necessari
Anche con un fingerprint HTTP/2 perfetto, lo spoofing del protocollo da solo non basta. I sistemi anti-bot moderni combinano fingerprinting a più livelli:
- Reputazione IP: datacenter IP ranges (ASNs di AWS, DigitalOcean, Hetzner) ricevono un punteggio base elevato. Anche con un fingerprint HTTP/2 impeccabile, una richiesta da un IP datacenter viene penalizzata.
- Geolocalizzazione: se il tuo JA4 dichiara Chrome su Windows ma l'IP è in un datacenter a Francoforte mentre l'Accept-Language è
en-US, l'incongruenza geografica aumenta il bot score. - Comportamento storico: i sistemi anti-bot mantengono un punteggio cumulativo per ogni IP. Un IP datacenter che ha servito migliaia di richieste automatizzate ha già un punteggio elevato prima della tua connessione.
I proxy residenziali risolvono questo problema fornendo IP registrati come residenziali nei database ASN (MaxMind, IP2Location). Un IP residenziale ha un punteggio base basso perché l'ASN corrisponde a un ISP domestico (Comcast, AT&T, Vodafone). Combinato con un fingerprint HTTP/2 coerente, il risultato è una richiesta indistinguibile da quella di un utente reale.
ProxyHat offre proxy residenziali con geolocalizzazione a livello di paese e città, che puoi combinare con il fingerprint del browser per massimizzare la coerenza. Consulta la pagina prezzi per i piani disponibili e la pagina locations per la copertura geografica.
Implementazione pratica con curl_cffi e ProxyHat
L'approccio più robusto per produrre un fingerprint HTTP/2 coerente in Python è utilizzare curl_cffi con impersonate="chrome" e instradare il traffico attraverso proxy residenziali ProxyHat. Ecco un esempio completo:
Installazione
pip install curl_cffi
Richiesta base con fingerprint Chrome
from curl_cffi import requests
# ProxyHat residential proxy — geolocalizzato negli Stati Uniti
proxy_url = "http://user-country-US:PASSWORD@gate.proxyhat.com:8080"
# Sessione con impersonazione Chrome 124
session = requests.Session(impersonate="chrome")
session.proxies = {"http": proxy_url, "https": proxy_url}
# Header coerenti con Chrome 124 su Windows
headers = {
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8",
"accept-language": "en-US,en;q=0.9",
"accept-encoding": "gzip, deflate, br, zstd",
"sec-ch-ua": '\"Chromium\";v=\"124\", \"Google Chrome\";v=\"124\", \"Not-A.Brand\";v=\"99\"',
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": '\"Windows\"',
"sec-fetch-dest": "document",
"sec-fetch-mode": "navigate",
"sec-fetch-site": "none",
"sec-fetch-user": "?1",
"upgrade-insecure-requests": "1",
}
response = session.get("https://example.com", headers=headers)
print(f"Status: {response.status_code}")
print(f"JA4+h2 fingerprint coerente con Chrome 124")
Sessioni sticky per scraping multi-pagina
Per lo SERP tracking o lo scraping di pagine che richiedono sessioni persistenti, usa l'opzione session nel username ProxyHat per mantenere lo stesso IP di uscita:
from curl_cffi import requests
# Sessione sticky — stesso IP per tutta la durata della sessione
proxy_url = "http://user-country-US-session-stable01:PASSWORD@gate.proxyhat.com:8080"
session = requests.Session(impersonate="chrome")
session.proxies = {"http": proxy_url, "https": proxy_url}
# Prima richiesta — ottiene cookie di sessione
r1 = session.get("https://target-site.com/login", headers=headers)
# Seconda richiesta — stesso IP, stessi cookie, stesso fingerprint
r2 = session.get("https://target-site.com/dashboard", headers=headers)
print(f"Login: {r1.status_code}, Dashboard: {r2.status_code}")
Verifica del fingerprint con curl
Puoi verificare rapidamente il tuo fingerprint HTTP/2 usando curl da riga di comando attraverso ProxyHat:
# Richiesta HTTP/2 attraverso ProxyHat con fingerprint Chrome
curl --http2 \
--proxy http://user-country-US:PASSWORD@gate.proxyhat.com:8080 \
-H "accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8" \
-H "accept-language: en-US,en;q=0.9" \
-H "sec-ch-ua: \"Chromium\";v=\"124\", \"Google Chrome\";v=\"124\"" \
-H "sec-ch-ua-mobile: ?0" \
-H "sec-ch-ua-platform: \"Windows\"" \
-H "sec-fetch-dest: document" \
-H "sec-fetch-mode: navigate" \
-H "sec-fetch-site: none" \
-H "upgrade-insecure-requests: 1" \
-o /dev/null -w "%{http_version} %{http_code} %{time_total}s\n" \
https://example.com
Il flag --http2 di curl utilizza nghttp2 con valori predefiniti che sono vicini a Chrome ma non identici. Per una corrispondenza esatta, usa curl-impersonate o curl_cffi con impersonate="chrome".
Rotazione IP per richieste ad alto volume
from curl_cffi import requests
import uuid
# Per ogni richiesta, genera un nuovo session ID → nuovo IP residenziale
def make_request(url, country="US"):
session_id = uuid.uuid4().hex[:12]
proxy_url = f"http://user-country-{country}-session-{session_id}:PASSWORD@gate.proxyhat.com:8080"
session = requests.Session(impersonate="chrome")
session.proxies = {"http": proxy_url, "https": proxy_url}
return session.get(url, headers=headers, timeout=30)
# 100 richieste con IP diversi e fingerprint Chrome coerente
for i in range(100):
r = make_request("https://target-site.com/page")
print(f"Request {i+1}: {r.status_code}")
Per dettagli completi sulla configurazione dei proxy, consulta la documentazione ufficiale di ProxyHat.
HTTP/3 e QUIC: il prossimo livello di fingerprinting
HTTP/3, basato su QUIC, introduce nuove superfici di fingerprinting. Il frame SETTINGS di HTTP/3 include parametri aggiuntivi come QPACK_MAX_TABLE_CAPACITY e MAX_FIELD_SECTION_SIZE. Inoltre, il transport parameters di QUIC (esposti nel handshake TLS 1.3) contengono segnali come initial_max_data, initial_max_stream_data_bidi_local, e max_idle_timeout.
Chrome 148 invia transport parameters QUIC con valori specifici (per esempio, initial_max_stream_data_bidi_local: 6291456) che differiscono da quelli di Firefox e Safari. I sistemi anti-bot stanno iniziando a fingerprintare anche questi parametri, estendendo il modello del fingerprinting HTTP/2 a HTTP/3.
Per ora, la maggior parte del traffico di scraping rimane su HTTP/2 perché il supporto QUIC richiede UDP (spesso bloccato dai proxy HTTP). Tuttavia, se il target supporta HTTP/3 e il tuo client non lo gestisce, l'assenza di un upgrade HTTP/3 può essere essa stessa un segnale di automazione — i browser reali tentano sempre QUIC prima di fallback su HTTP/2.
Uso appropriato e considerazioni legali
Il fingerprinting HTTP/2 e l'uso di proxy residenziali per presentare un'identità coerente sono tecniche legittime in diversi contesti:
- Ricerca di sicurezza autorizzata: penetration testing con scope scritto, bug bounty program, analisi di sistemi anti-bot per ricerca accademica.
- Monitoraggio di prezzi e disponibilità: raccolta di dati pubblici da siti e-commerce che non vietano esplicitamente l'accesso automatizzato nei loro ToS.
- Verifica di ad placement e SERP: controllo del posizionamento organico e a pagamento da diverse geolocalizzazioni.
- QA e test di infrastrutture: simulazione di traffico realistico per testare la propria infrastruttura.
Tuttavia, queste tecniche non devono essere usate per:
- Aggirare controlli di accesso o autenticazione non autorizzati (violazione del CFAA negli Stati Uniti).
- Raccogliere dati personali in violazione del GDPR nell'UE o del CCPA in California.
- Condurre frodi pubblicitarie, credential stuffing, o attacchi DDoS.
- Violare i Terms of Service di un sito in modo che causi danni.
Consulta sempre il robots.txt e i ToS del target, e ottieni autorizzazione scritta quando possibile. Per approfondimenti sull'uso etico dei proxy, visita la pagina web scraping.
Errori comuni e casi limite
Errore 1: Modificare solo l'User-Agent
Il caso più comune: sviluppatori che impostano User-Agent: Mozilla/5.0... Chrome/124 su httpx e pensano di aver risolto. Il fingerprint HTTP/2 rimane quello di h2 con HEADER_TABLE_SIZE 4096. Il server rileva la discrepanza in meno di 200ms.
Errore 2: Usare TLS spoofing senza h2 spoofing
Alcuni sviluppatori usano librerie come tls-client o patchano l'ordine dei cipher TLS per ottenere un JA4 Chrome, ma continuano a usare httpx per le richieste HTTP/2. Il JA4 è coerente, ma il SETTINGS frame no. Il bot score rimane massimo.
Errore 3: Proxy datacenter con fingerprint perfetto
Un fingerprint HTTP/2 impeccabile da un IP AWS us-east-1 viene comunque flaggato. La reputazione IP ha un peso del 30-40% nel punteggio finale di molti sistemi anti-bot. Senza un proxy residenziale, il fingerprint è inutile.
Caso limite: HTTP/3 fallback
Se il target supporta HTTP/3 e il tuo client non lo gestisce, l'assenza di un upgrade HTTP/3 può essere essa stessa un segnale. I browser reali tentano QUIC prima di fallback su HTTP/2. Considera l'uso di browser headless (Playwright, Puppeteer) per target che richiedono HTTP/3.
Caso limite: Canvas fingerprint e segnali JavaScript
Il fingerprinting HTTP/2 è solo il primo livello. Se la pagina carica JavaScript, il server può verificare canvas fingerprint, WebGL renderer, font enumeration, e segnali comportamentali (mouse movement, typing cadence). Per i target più protetti, un browser headless con proxy residenziali è l'unica soluzione completa.
Key Takeaways
- Il fingerprinting HTTP/2 ispeziona il frame SETTINGS (HEADER_TABLE_SIZE, INITIAL_WINDOW_SIZE, MAX_CONCURRENT_STREAMS), WINDOW_UPDATE, stream priority e l'ordine degli pseudo-header (m,a,s,p per Chrome).
- L'Akamai h2 fingerprint compatta questi segnali in una stringa come
1:65536,2:0,3:1000,4:6291456,6:262144|15663105|0|m,a,s,p. JA4H estende il fingerprinting al livello HTTP applicativo.- Un client con JA4 Chrome ma SETTINGS frame di httpx (HEADER_TABLE_SIZE 4096) riceve il massimo bot score prima del caricamento dell'HTML.
- curl_cffi con
impersonate="chrome"produce sia il JA4 che l'h2 fingerprint di Chrome — è la soluzione più robusta in Python.- I proxy residenziali sono necessari perché la reputazione IP contribuisce per il 30-40% al bot score finale. Un fingerprint perfetto da un IP datacenter viene comunque flaggato.
- Usa queste tecniche solo per ricerca autorizzata, monitoraggio legittimo e QA. Rispetta robots.txt, ToS, GDPR e CFAA.
Pronto a implementare fingerprint HTTP/2 coerenti con proxy residenziali? Esplora i piani ProxyHat e inizia a instradare il tuo traffico attraverso IP residenziali geolocalizzati con gate.proxyhat.com:8080.






