Perché il proxy funziona nel browser ma non nel codice: analisi completa del problema
Situazione classica: configuri un proxy nel browser, apri un sito — tutto funziona. Avvii uno script con lo stesso proxy — errore di connessione, timeout o blocco. Vediamo perché succede e come risolverlo.
In cosa differisce una richiesta dal browser rispetto a una dal codice
Quando apri un sito nel browser tramite un proxy, succede molto di più di una semplice richiesta HTTP. Il browser esegue automaticamente:
- L'invio di un set completo di intestazioni (User-Agent, Accept, Accept-Language, Accept-Encoding)
- L'esecuzione dell'handshake TLS con un set di cifrari appropriato
- La gestione di reindirizzamenti e cookie
- L'esecuzione di JavaScript e il caricamento delle risorse dipendenti
- La memorizzazione nella cache delle risposte DNS e dei certificati
Una richiesta minima dal codice appare molto diversa per il server: assomiglia a un robot, non a un utente umano. Anche se il proxy funziona correttamente, il sito di destinazione potrebbe bloccare specificamente il tuo script.
Problemi di autenticazione del proxy
La causa più comune è il passaggio errato di login e password. Il browser mostra una finestra pop-up per inserire le credenziali, mentre nel codice questo deve essere fatto esplicitamente.
Formato URL errato
Un errore frequente è l'omissione dello schema o la codifica errata dei caratteri speciali:
# Errato
proxy = "user:pass@proxy.example.com:8080"
# Corretto
proxy = "http://user:pass@proxy.example.com:8080"
# Se la password contiene caratteri speciali (@, :, /)
from urllib.parse import quote
password = quote("p@ss:word/123", safe="")
proxy = f"http://user:{password}@proxy.example.com:8080"
Autenticazione basata su IP vs login/password
Alcuni fornitori di proxy utilizzano una whitelist basata sull'indirizzo IP. Il browser sul tuo computer funziona perché il tuo IP è nella whitelist. Ma lo script su un server no, perché il server ha un IP diverso.
Verifica nel pannello del provider quale metodo di autenticazione viene utilizzato e quali IP sono aggiunti alla whitelist.
Incongruenza dei protocolli HTTP/HTTPS/SOCKS
Il browser spesso determina automaticamente il tipo di proxy. Nel codice, è necessario specificarlo esplicitamente, e un errore nel protocollo porta a un rifiuto silenzioso.
| Tipo di Proxy | Schema nell'URL | Caratteristiche |
|---|---|---|
| Proxy HTTP | http:// |
Funziona per HTTP e HTTPS tramite CONNECT |
| Proxy HTTPS | https:// |
Connessione crittografata al proxy |
| SOCKS4 Proxy | socks4:// |
Senza autenticazione, solo IPv4 |
| SOCKS5 Proxy | socks5:// |
Con autenticazione, UDP, IPv6 |
| SOCKS5h Proxy | socks5h:// |
Risoluzione DNS tramite proxy |
È fondamentale: se hai un proxy SOCKS5 ma specifichi http://, la connessione non verrà stabilita. La libreria tenterà di comunicare tramite protocollo HTTP con un server SOCKS.
Intestazioni mancanti e fingerprinting
Anche se il proxy funziona correttamente, il sito di destinazione potrebbe bloccare la richiesta a causa di intestazioni sospette. Confrontiamo:
Richiesta dal browser
GET /api/data HTTP/1.1
Host: example.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36...
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1
Richiesta predefinita con requests
GET /api/data HTTP/1.1
Host: example.com
User-Agent: python-requests/2.28.0
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive
La differenza è evidente. Un sito con protezione anti-bot identificherà immediatamente che la richiesta non proviene da un browser.
Set minimo di intestazioni per mascherarsi
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
"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",
"Connection": "keep-alive",
"Upgrade-Insecure-Requests": "1",
"Sec-Fetch-Dest": "document",
"Sec-Fetch-Mode": "navigate",
"Sec-Fetch-Site": "none",
"Sec-Fetch-User": "?1",
"Cache-Control": "max-age=0"
}
Certificati SSL e verifica
Il browser dispone di un archivio integrato di certificati root ed è in grado di gestire varie configurazioni SSL. Nel codice possono sorgere problemi:
Errore SSL: CERTIFICATE_VERIFY_FAILED
Alcuni proxy utilizzano i propri certificati per ispezionare il traffico. Il browser potrebbe avere questo certificato come attendibile, ma il tuo script no.
# Soluzione temporanea per il debug (NON per la produzione!)
import requests
response = requests.get(url, proxies=proxies, verify=False)
# Soluzione corretta — specificare il percorso del certificato
response = requests.get(url, proxies=proxies, verify="/path/to/proxy-ca.crt")
Importante: Disabilitare la verifica SSL (
verify=False) rende la connessione vulnerabile agli attacchi MITM. Usare solo per il debug in un ambiente sicuro.
TLS Fingerprint
I sistemi anti-bot avanzati analizzano il TLS fingerprint — l'ordine e il set di cifrari utilizzati durante l'instaurazione della connessione. Python requests utilizza un set standard che differisce da quello del browser.
Per aggirare questo problema, utilizzare librerie con un TLS fingerprint personalizzato:
# Installazione: pip install curl-cffi
from curl_cffi import requests
response = requests.get(
url,
proxies={"https": proxy},
impersonate="chrome120" # Simula il TLS fingerprint di Chrome 120
)
Fughe di DNS e risoluzione
Un altro problema non ovvio è la risoluzione DNS. Quando si utilizza un proxy HTTP, la richiesta DNS può partire direttamente dalla tua macchina, bypassando il proxy.
Come influisce sul funzionamento
- Il sito vede il resolver DNS reale, non il proxy
- La geolocalizzazione viene determinata in modo errato
- Alcuni siti bloccano l'incongruenza tra IP e regione DNS
Soluzione per SOCKS5
Utilizza lo schema socks5h:// invece di socks5:// — la "h" indica che la risoluzione DNS verrà eseguita sul lato proxy:
# DNS risolto localmente (fuga!)
proxy = "socks5://user:pass@proxy.example.com:1080"
# DNS risolto tramite proxy (corretto)
proxy = "socks5h://user:pass@proxy.example.com:1080"
Esempi funzionanti per Python, Node.js e cURL
Python con requests
import requests
from urllib.parse import quote
# Dati del proxy
proxy_host = "proxy.example.com"
proxy_port = "8080"
proxy_user = "username"
proxy_pass = quote("p@ssword!", safe="") # Codifica i caratteri speciali
# Formazione dell'URL del proxy
proxy_url = f"http://{proxy_user}:{proxy_pass}@{proxy_host}:{proxy_port}"
proxies = {
"http": proxy_url,
"https": proxy_url
}
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Accept-Language": "en-US,en;q=0.9",
"Accept-Encoding": "gzip, deflate, br",
}
try:
response = requests.get(
"https://httpbin.org/ip",
proxies=proxies,
headers=headers,
timeout=30
)
print(f"Status: {response.status_code}")
print(f"IP: {response.json()}")
except requests.exceptions.ProxyError as e:
print(f"Errore proxy: {e}")
except requests.exceptions.ConnectTimeout:
print("Timeout di connessione al proxy")
Python con aiohttp (asincrono)
import aiohttp
import asyncio
async def fetch_with_proxy():
proxy_url = "http://user:pass@proxy.example.com:8080"
async with aiohttp.ClientSession() as session:
async with session.get(
"https://httpbin.org/ip",
proxy=proxy_url,
headers={"User-Agent": "Mozilla/5.0..."}
) as response:
return await response.json()
result = asyncio.run(fetch_with_proxy())
print(result)
Node.js con axios
const axios = require('axios');
const HttpsProxyAgent = require('https-proxy-agent');
const proxyUrl = 'http://user:pass@proxy.example.com:8080';
const agent = new HttpsProxyAgent(proxyUrl);
axios.get('https://httpbin.org/ip', {
httpsAgent: agent,
headers: {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36...'
}
})
.then(response => console.log(response.data))
.catch(error => console.error('Error:', error.message));
Node.js con node-fetch e SOCKS
const fetch = require('node-fetch');
const { SocksProxyAgent } = require('socks-proxy-agent');
const agent = new SocksProxyAgent('socks5://user:pass@proxy.example.com:1080');
fetch('https://httpbin.org/ip', { agent })
.then(res => res.json())
.then(data => console.log(data));
cURL
# Proxy HTTP
curl -x "http://user:pass@proxy.example.com:8080" \
-H "User-Agent: Mozilla/5.0..." \
https://httpbin.org/ip
# Proxy SOCKS5 con DNS tramite proxy
curl --socks5-hostname "proxy.example.com:1080" \
--proxy-user "user:pass" \
https://httpbin.org/ip
# Debug - mostra l'intero processo di connessione
curl -v -x "http://user:pass@proxy.example.com:8080" \
https://httpbin.org/ip
Checklist di Diagnosi
Se il proxy non funziona nel codice, controlla in sequenza:
- Formato URL del proxy — è presente lo schema (http://, socks5://)?
- Caratteri speciali nella password — sono codificati tramite URL-encoding?
- Tipo di proxy — il protocollo specificato corrisponde a quello reale?
- Autenticazione — tramite IP o tramite login? L'IP del server è nella whitelist?
- Intestazioni — sono state aggiunte le intestazioni del browser (User-Agent, ecc.)?
- SSL — ci sono errori di certificato?
- DNS — è stato utilizzato socks5h:// per la risoluzione tramite proxy?
- Timeout — è stato concesso abbastanza tempo per la connessione (specialmente per i proxy residenziali)?
Conclusione
La differenza tra browser e codice risiede nei dettagli: intestazioni, protocolli, SSL, DNS. Il browser nasconde questa complessità, mentre nel codice ogni aspetto deve essere configurato esplicitamente. Inizia controllando il formato dell'URL e l'autenticazione, quindi aggiungi le intestazioni del browser: questo risolve il 90% dei problemi.
Per compiti di scraping e automazione dove sono cruciali stabilità e bassa percentuale di blocco, i proxy residenziali sono una buona scelta — puoi saperne di più su proxycove.com.