Pourquoi le proxy fonctionne dans le navigateur, mais pas dans le code : analyse complète du problème
Situation classique : vous configurez un proxy dans votre navigateur, ouvrez un site — tout fonctionne. Vous lancez un script avec le même proxy — erreur de connexion, timeout ou bannissement. Nous allons voir pourquoi cela se produit et comment y remédier.
En quoi une requête du navigateur diffère-t-elle d'une requête depuis le code
Lorsque vous ouvrez un site dans un navigateur via un proxy, il se passe bien plus qu'une simple requête HTTP. Le navigateur effectue automatiquement les actions suivantes :
- Envoi d'un ensemble complet d'en-têtes (User-Agent, Accept, Accept-Language, Accept-Encoding)
- Exécution du TLS handshake avec un jeu de chiffrements approprié
- Gestion des redirections et des cookies
- Exécution de JavaScript et chargement des ressources dépendantes
- Mise en cache des réponses DNS et des certificats
Une requête minimale depuis le code apparaît très différente pour le serveur — elle ressemble à celle d'un robot, pas d'un humain. Même si le proxy fonctionne correctement, le site cible peut bloquer spécifiquement votre script.
Problèmes d'authentification du proxy
La cause la plus fréquente est la transmission incorrecte du nom d'utilisateur et du mot de passe. Le navigateur affiche une fenêtre contextuelle pour saisir les identifiants, alors que dans le code, cela doit être fait explicitement.
Format URL incorrect
Une erreur courante est l'omission du schéma ou un mauvais échappement des caractères spéciaux :
# Incorrect
proxy = "user:pass@proxy.example.com:8080"
# Correct
proxy = "http://user:pass@proxy.example.com:8080"
# Si le mot de passe contient des caractères spéciaux (@, :, /)
from urllib.parse import quote
password = quote("p@ss:word/123", safe="")
proxy = f"http://user:{password}@proxy.example.com:8080"
Authentification par IP vs. nom d'utilisateur/mot de passe
Certains fournisseurs de proxy utilisent une liste blanche basée sur l'adresse IP. Le navigateur fonctionne sur votre ordinateur parce que votre IP est ajoutée à la liste blanche. Mais le script sur le serveur ne fonctionne pas, car le serveur a une autre IP.
Vérifiez dans le panneau du fournisseur quel type d'authentification est utilisé et quelles IPs sont ajoutées à la liste blanche.
Inadéquation des protocoles HTTP/HTTPS/SOCKS
Le navigateur détermine souvent automatiquement le type de proxy. Dans le code, vous devez le spécifier explicitement, et une erreur de protocole entraîne un échec silencieux.
| Type de Proxy | Schéma dans l'URL | Particularités |
|---|---|---|
| Proxy HTTP | http:// |
Fonctionne pour HTTP et HTTPS via CONNECT |
| Proxy HTTPS | https:// |
Connexion chiffrée au proxy |
| SOCKS4 Proxy | socks4:// |
Sans authentification, IPv4 uniquement |
| SOCKS5 Proxy | socks5:// |
Avec authentification, UDP, IPv6 |
| SOCKS5h Proxy | socks5h:// |
Résolution DNS via le proxy |
Point critique : si vous avez un proxy SOCKS5 mais que vous spécifiez http://, la connexion ne s'établira pas. La bibliothèque tentera de communiquer en protocole HTTP avec un serveur SOCKS.
Absence d'en-têtes et empreinte numérique (fingerprint)
Même si le proxy fonctionne correctement, le site cible peut bloquer la requête en raison d'en-têtes suspects. Comparons :
Requête depuis le navigateur
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
Requête par défaut avec 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 différence est évidente. Un site avec une protection anti-bot identifiera instantanément qu'une requête ne provient pas d'un navigateur.
Ensemble minimal d'en-têtes pour le déguisement
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"
}
Certificats SSL et vérification
Le navigateur dispose d'un magasin de certificats racine intégré et sait gérer diverses configurations SSL. Des problèmes peuvent survenir dans le code :
Erreur SSL : CERTIFICATE_VERIFY_FAILED
Certains proxies utilisent leurs propres certificats pour inspecter le trafic. Votre navigateur peut avoir ce certificat comme approuvé, mais votre script non.
# Solution temporaire pour le débogage (PAS pour la production !)
import requests
response = requests.get(url, proxies=proxies, verify=False)
# Solution correcte — spécifier le chemin vers le certificat
response = requests.get(url, proxies=proxies, verify="/path/to/proxy-ca.crt")
Important : Désactiver la vérification SSL (
verify=False) rend la connexion vulnérable aux attaques MITM. À n'utiliser que pour le débogage dans un environnement sécurisé.
Empreinte TLS (TLS Fingerprint)
Les systèmes anti-bots avancés analysent l'empreinte TLS — l'ordre et l'ensemble des chiffrements lors de l'établissement de la connexion. Python requests utilise un ensemble standard qui diffère de celui du navigateur.
Pour contourner cela, utilisez des bibliothèques avec une empreinte TLS personnalisée :
# Installation : pip install curl-cffi
from curl_cffi import requests
response = requests.get(
url,
proxies={"https": proxy},
impersonate="chrome120" # Imite l'empreinte TLS de Chrome 120
)
Fuites DNS et résolution
Un autre problème subtil est la résolution DNS. Lors de l'utilisation d'un proxy HTTP, la requête DNS peut être envoyée directement depuis votre machine, contournant le proxy.
Impact sur le fonctionnement
- Le site voit le résolveur DNS réel, et non le proxy
- La géolocalisation est déterminée de manière incorrecte
- Certains sites bloquent les incohérences entre la région IP et la région DNS
Solution pour SOCKS5
Utilisez le schéma socks5h:// au lieu de socks5:// — la lettre "h" signifie que la résolution DNS sera effectuée côté proxy :
# DNS résolu localement (fuite !)
proxy = "socks5://user:pass@proxy.example.com:1080"
# DNS résolu via le proxy (correct)
proxy = "socks5h://user:pass@proxy.example.com:1080"
Exemples fonctionnels pour Python, Node.js et cURL
Python avec requests
import requests
from urllib.parse import quote
# Données du proxy
proxy_host = "proxy.example.com"
proxy_port = "8080"
proxy_user = "username"
proxy_pass = quote("p@ssword!", safe="") # Échapper les caractères spéciaux
# Construction de l'URL du 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"Statut : {response.status_code}")
print(f"IP : {response.json()}")
except requests.exceptions.ProxyError as e:
print(f"Erreur de proxy : {e}")
except requests.exceptions.ConnectTimeout:
print("Délai d'attente de connexion au proxy")
Python avec aiohttp (asynchrone)
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 avec 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('Erreur:', error.message));
Node.js avec node-fetch et 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 avec résolution DNS via le proxy
curl --socks5-hostname "proxy.example.com:1080" \
--proxy-user "user:pass" \
https://httpbin.org/ip
# Débogage — afficher tout le processus de connexion
curl -v -x "http://user:pass@proxy.example.com:8080" \
https://httpbin.org/ip
Liste de contrôle de diagnostic
Si le proxy ne fonctionne pas dans le code, vérifiez dans l'ordre :
- Format de l'URL du proxy — le schéma (http://, socks5://) est-il présent ?
- Caractères spéciaux dans le mot de passe — sont-ils encodés en URL ?
- Type de proxy — le protocole spécifié correspond-il au protocole réel ?
- Authentification — par IP ou par nom d'utilisateur ? L'IP du serveur est-elle dans la liste blanche ?
- En-têtes — l'User-Agent et d'autres en-têtes de navigateur ont-ils été ajoutés ?
- SSL — y a-t-il des erreurs de certificat ?
- DNS — utilisez-vous socks5h:// pour la résolution via le proxy ?
- Délais d'attente (Timeouts) — y a-t-il suffisamment de temps pour la connexion (surtout pour les proxies résidentiels) ?
Conclusion
La différence entre le navigateur et le code réside dans les détails : en-têtes, protocoles, SSL, DNS. Le navigateur masque cette complexité, mais dans le code, chaque aspect doit être configuré explicitement. Commencez par vérifier le format de l'URL et l'authentification, puis ajoutez les en-têtes de navigateur — cela résout 90 % des problèmes.
Pour les tâches de scraping et d'automatisation où la stabilité et un faible taux de blocage sont cruciaux, les proxies résidentiels sont bien adaptés — vous pouvez en savoir plus sur proxycove.com.