ProxyError van Requests bij een privé-API? Controleer eerst de omgeving
Requests voegt proxy-, auth- en CA-instellingen uit de omgeving samen. Lees wat het proces ziet, sluit de privéhost uit en houd TLS-verificatie aan.
De VPN is actief, het interne dashboard opent in een browser, en het Python-script gooit nog steeds een proxyfout. Aan de tunnel is in dat beeld niets mis: de interpreter leest een proxyadres uit zijn eigen omgeving en stuurt het verzoek daarheen. Deze runbook leest de omgeving die het proces werkelijk ziet, past de smalste uitsluiting toe en overweegt pas daarna een expliciet sessiebeleid. Elke stap eindigt met een commando waarvan de uitvoer bepaalt of je doorgaat.
Lees de omgeving die het proces werkelijk ziet
Proxyinstellingen van de desktop, shell-exports en de Environment=-regels van een systemd-unit zijn drie verschillende bronnen, en Requests leest alleen de laatste die het erft. Voer de diagnose hieronder uit in dezelfde shell, container of unit die het falende script draait, niet in je interactieve terminal. Een variabele die in je login-shell is geëxporteerd, is onzichtbaar voor een containerproces, en dat gat verklaart veel meldingen over een proxy die uit het niets verschijnt. De twee commando's printen dezelfde gegevens op verschillende detailniveaus.
env | grep -i -E 'proxy|requests_ca_bundle|curl_ca_bundle'
python3 -c "import os;[print(k,'=',v) for k,v in sorted(os.environ.items()) if 'proxy' in k.lower()]"
Beide spellingen van elke variabele worden gelezen, en dubbele vermeldingen worden toegepast in de volgorde waarin het proces ze ontving, dus een verouderde HTTP_PROXY kan de waarde die je zojuist hebt geëxporteerd overschrijven. De CA-bundelvariabelen zijn hier ook belangrijk, omdat ze veranderen waar verify naar resolveert zonder ergens in de code te verschijnen. Is de uitvoer leeg, dan is de omgeving niet je probleem en kun je stoppen met dit gedeelte. Als er een proxy in staat die je niet herkent, zoek dan het bestand of de unit op die hem instelt voordat je iets verandert.
Vraag Requests wat het met een specifieke URL zou doen. Deze aanroep toont de samengevoegde proxy-, verify- en cert-waarden voor dat verzoek zonder een pakket te versturen.
python3 -c "import requests;s=requests.Session();print(s.merge_environment_settings('https://api.corp.example/status',{},None,None,None))"
Een niet-lege proxies-vermelding voor de privéhost is je antwoord, en de geprinte verify-waarde vertelt op welke CA-bundel het verzoek zou vertrouwen. Als beide correct lijken, zit de fout later in de stack en moet je naar het verificatiegedeelte springen. Raad niet uit de code: de samenvoeging gebeurt per verzoek, waardoor één sessie zich voor twee hosts anders kan gedragen.
Sluit de privéhost uit voordat je iets uitschakelt
Wanneer de proxy echt nodig is voor andere bestemmingen, is de smalle oplossing een uitsluiting voor deze host. Voeg toe aan de bestaande lijst in plaats van die te vervangen, want het wissen van NO_PROXY breekt elke andere interne bestemming die erop vertrouwde. De commando's hieronder voegen de vermelding toe en vragen Requests daarna of de omzeiling geldt.
export NO_PROXY="${NO_PROXY:+$NO_PROXY,}api.corp.example,.corp.example"
python3 -c "import requests;print(requests.utils.should_bypass_proxies('https://api.corp.example/status',None))"
Overeenkomst is op host-suffix, dus .corp.example dekt het hele domein terwijl api.corp.example één naam dekt. Een kale asterisk schakelt proxy uit voor elke host en is bijna nooit wat een productietaak wil. Of een CIDR-vermelding wordt gerespecteerd hangt af van de Requests- en urllib3-versies in de image, dus test de exacte vermelding in plaats van aan te nemen dat het werkt. Controleer het resultaat van should_bypass_proxies voor de echte URL, niet voor een hostnaam die je uit je geheugen hebt getypt.
Houd de twee lagen gescheiden in je hoofd. Het omzeilen van de HTTP-proxy omzeilt niet de VPN, omdat de proxybeslissing in de bibliotheek valt en de tunnelbeslissing in de routingtabel van de kernel.
Schakel trust_env alleen uit als bewust sessiebeleid
Als de hele taak de omgevingsconfiguratie moet negeren, bouw de sessie dan expliciet op en leg de beslissing vast in de code. Dat is beleid, geen omweg, en het hoort in een diff die een reviewer kan zien. Het voorbeeld hieronder behoudt een timeout, een expliciet CA-pad en een statuscontrole.
import requests
with requests.Session() as session:
session.trust_env = False
response = session.get(
"https://api.corp.example/status",
timeout=(5, 20),
verify="/etc/ssl/certs/corp-ca.pem",
)
response.raise_for_status()
Het uitschakelen van trust_env verandert drie gedragingen tegelijk, en slechts één daarvan gaat over proxy's. Requests stopt met het lezen van .netrc-referenties, en de variabelen REQUESTS_CA_BUNDLE en CURL_CA_BUNDLE beïnvloeden verify niet meer, dat dan terugvalt op de meegeleverde certificaatopslag. Als je organisatie TLS beëindigt met een eigen CA, levert die terugval een verificatiefout op die niets met de zojuist gemaakte wijziging lijkt te maken te hebben. Het expliciet doorgeven van verify is wat de twee effecten gescheiden houdt.
Wat je niet moet doen, is verify=False instellen om de resulterende fout te dempen. Dat ruilt een verkeerde proxyconfiguratie in voor een niet-geverifieerde verbinding, en het verbergt het certificaatprobleem dat de uitsluiting moest blootleggen.
Verifieer de laag die faalt en houd een terugdraai achter de hand
Drie fouten zien er in een log hetzelfde uit en hebben niets gemeen: een proxy die de bestemming niet bereikt, gooit ProxyError of geeft 502 terug, een vertrouwensprobleem gooit SSLError, en een afwijzing door de applicatie geeft 401 of 403 terug. Reproduceer het verzoek buiten Requests om te zien welke je hebt. De commando's hieronder verwijderen de proxy, testen de certificaatketen en herhalen daarna de aanroep via de bibliotheek.
curl -sv --noproxy '*' https://api.corp.example/status -o /dev/null
openssl s_client -connect api.corp.example:443 -servername api.corp.example </dev/null
python3 -c "import requests;r=requests.get('https://api.corp.example/status',timeout=10);print(r.status_code)"
Als de curl-aanroep slaagt en de Python-aanroep faalt, is de omgeving nog steeds het verschil. Als beide in de TLS-fase falen, was de proxy nooit het probleem en is de certificaatketen het. Als beide 401 teruggeven, heeft het verzoek de applicatie bereikt en is het resterende werk authenticatie. Noteren welke van de drie je hebt gezien, bespaart de volgende persoon een uur.
Terugdraaien is één regel in beide richtingen: verwijder de uitsluiting, of zet trust_env terug. Bewaar de vorige NO_PROXY-waarde voordat je er iets aan toevoegt, en geef de voorkeur aan een wrapperscript of een Environment=-vermelding op unitniveau boven een export die alleen in je terminalgeschiedenis leeft. Beide wijzigingen zijn zichtbaar in de procesomgeving, dus een herstart brengt je meestal terug bij het oude gedrag.
Sluit af met vier controles: de samengevoegde instellingen voor de privé-URL tonen geen proxy, het certificaat valideert tegen de bedoelde CA, het verzoek geeft de status terug die je verwacht, en de wijziging is gecommit op een plek die reproduceerbaar is. Een groen script met een onverklaarde omgeving is een oplossing die op de volgende machine weer breekt. De commando's in deze runbook zijn degene die je in het ticket plakt.