Die Umgebungsvariablen HTTP_PROXY, HTTPS_PROXY und NO_PROXY erscheinen auf den ersten Blick sehr einfach: Variable gesetzt — der gesamte Traffic läuft über den Proxy. In der Praxis verlieren Entwickler genau an dieser Stelle Stunden mit Debugging: Der Proxy funktioniert im Terminal, aber nicht im Docker-Container; curl läuft über den Proxy, die Python-Anwendung jedoch nicht; der CI-Runner blockiert plötzlich das interne Registry, weil NO_PROXY mit falscher Syntax angegeben ist. Lassen Sie uns 7 reale Fallstricke untersuchen und zeigen, wie man sie mit konkretem Code umgeht.
Wie Anwendungen Proxy-Variablen lesen
Es ist wichtig zu verstehen: HTTP_PROXY, HTTPS_PROXY und NO_PROXY sind kein Standard des HTTP-Protokolls, sondern eine Konvention, die jede Bibliothek auf ihre Weise umsetzt. Der Linux-Kernel weiß nichts über diese Variablen — sie werden von einer bestimmten Anwendung oder Bibliothek während der Initialisierung des Netzwerkclients gelesen.
Zum Beispiel liest curl und die auf libcurl basierenden Tools die Variablen bei jedem Start des Prozesses. Die Python-Bibliothek requests liest sie über das Modul urllib.request.getproxies(), das die Umgebung des Prozesses zum Zeitpunkt der Erstellung der Sitzung scannt. Der integrierte fetch() in Node.js berücksichtigt diese Variablen bis Version 18 überhaupt nicht ohne zusätzliche Pakete.
Daher gilt die erste Regel: Die Proxy-Variable funktioniert nur dort, wo sie ausdrücklich von einer bestimmten Bibliothek unterstützt wird. Eine Überprüfung über curl garantiert nicht, dass Ihre Anwendung in Go, Java oder Node.js sich gleich verhält.
export HTTP_PROXY="http://user:[email protected]:8080"
export HTTPS_PROXY="http://user:[email protected]:8080"
export NO_PROXY="localhost,127.0.0.1,.internal.corp"
curl -v https://api.example.com/ping
# curl zeigt im verbose-Log: "Trying proxy.example.com..."
Fallstrick 1: Groß- und Kleinschreibung der Variablen und die httpoxy-Sicherheitsanfälligkeit
Die meisten Konsolen-Tools in Linux lesen die Variablen in Kleinbuchstaben: http_proxy, https_proxy, no_proxy. Viele moderne SDKs orientieren sich an Großbuchstaben. Der Unterschied scheint gering zu sein, aber genau wegen ihm treten die Hälfte der Bugs im Stil von "funktioniert lokal, funktioniert nicht auf dem Server" bereits am ersten Tag der Einrichtung auf.
Eine besondere Geschichte ist die httpoxy-Sicherheitsanfälligkeit (CVE-2016-5385 und verwandte). Der Kern der Sache ist, dass die CGI-Umgebung (PHP, Python WSGI, einige Ruby-Frameworks) automatisch den HTTP-Header Proxy aus der eingehenden Anfrage in die Umgebungsvariable HTTP_PROXY umwandelt. Ein Angreifer sendet den Header Proxy: http://attacker.com, und Ihre Serveranwendung beginnt, ausgehende Anfragen über einen fremden Proxy-Server zu senden, was den Weg für MITM-Angriffe und Datenlecks öffnet.
Die Lösung besteht darin, die Variablen immer in beiden Schreibweisen zu setzen und HTTP_PROXY auf der Ebene des Webservers für eingehende Anfragen ausdrücklich zu nullen:
# In beiden Schreibweisen für Kompatibilität setzen
export http_proxy="http://proxy.example.com:8080"
export HTTP_PROXY="http://proxy.example.com:8080"
export https_proxy="http://proxy.example.com:8080"
export HTTPS_PROXY="http://proxy.example.com:8080"
# In nginx — blockieren Sie den Proxy-Header vom Client
proxy_set_header Proxy "";
Fallstrick 2: Die Syntax von NO_PROXY variiert zwischen den Tools
NO_PROXY ist eine Liste von Ausnahmen, aber das Format zur Angabe von Domains und Masken ist nicht standardisiert. Einige Tools verstehen .example.com als "jede Subdomain", andere verlangen *.example.com, wieder andere verstehen nur die genaue Übereinstimmung des Hosts ohne Wildcard.
Zum Beispiel unterstützt die Go-Bibliothek net/http die CIDR-Notation in NO_PROXY (10.0.0.0/8), während der klassische curl bis zu bestimmten Versionen dies nicht tut. Dies führt zu Situationen, in denen dasselbe NO_PROXY das interne Registry für einen CI-Schritt korrekt ausschließt und für einen anderen, der in einer anderen Sprache geschrieben ist, nicht ausschließt.
| Tool | Wildcard-Subdomain | CIDR-Maske |
|---|---|---|
| curl / libcurl | über .example.com | nein |
| Go net/http | über .example.com | ja |
| Python requests | über .example.com | nein |
| npm / Node.js | benötigt *.example.com | nein |
Praktischer Rat: Listen Sie in NO_PROXY sowohl die Domain- als auch die Wildcard-Variante gleichzeitig auf, um verschiedene Parser abzudecken:
export NO_PROXY="localhost,127.0.0.1,.internal.corp,*.internal.corp,10.0.0.0/8"
Fallstrick 3: Docker build vs Docker run — unterschiedliche Umgebungsvariablen
Dies ist wohl der häufigste Fallstrick in Docker. Variablen, die über docker run -e HTTP_PROXY=... gesetzt werden, wirken nur während der Laufzeit des Containers. Sie gelangen NICHT automatisch in die Build-Phase des Images. Wenn Ihr Dockerfile innerhalb apt-get install oder pip install aufruft und der Build hinter einem Unternehmensproxy erfolgt, wird der Build einfach aufgrund eines Timeouts hängen bleiben.
Für den Build müssen Build-Args übergeben werden, die Docker seit Version 1.9+ "out of the box" ohne Deklaration im Dockerfile versteht:
docker build \
--build-arg HTTP_PROXY=http://proxy.example.com:8080 \
--build-arg HTTPS_PROXY=http://proxy.example.com:8080 \
--build-arg NO_PROXY=localhost,127.0.0.1 \
-t my-app:latest .
Wenn Sie möchten, dass die Variable auch innerhalb des gestarteten Containers verfügbar bleibt, müssen Sie sie ausdrücklich über ENV im Dockerfile duplizieren — ARG und ENV sind in Docker grundsätzlich unterschiedliche Mechanismen, und ARG verschwindet sofort nach dem Erstellen der Schicht:
ARG HTTP_PROXY
ARG HTTPS_PROXY
ARG NO_PROXY
ENV HTTP_PROXY=${HTTP_PROXY}
ENV HTTPS_PROXY=${HTTPS_PROXY}
ENV NO_PROXY=${NO_PROXY}
RUN apt-get update && apt-get install -y curl
Ein weiterer Punkt: Der Docker-Daemon kann selbst einen Proxy verwenden, um Images aus dem Registry herunterzuladen — dies wird nicht im Dockerfile, sondern in der Konfiguration der systemd-Einheit /etc/systemd/system/docker.service.d/http-proxy.conf konfiguriert, was oft mit Variablen innerhalb des Containers verwechselt wird.
Fallstrick 4: docker-compose und Vererbung von Variablen zwischen Diensten
In docker-compose werden die Umgebungsvariablen des Hosts NICHT automatisch in die Container weitergegeben, es sei denn, Sie haben sie ausdrücklich im Abschnitt environment angegeben oder verwenden eine Datei .env. Ein häufiger Fehler ist es, den Proxy im Terminal vor dem Start von docker-compose up zu setzen und zu erwarten, dass alle Dienste ihn übernehmen.
version: "3.9"
services:
scraper:
build:
context: .
args:
HTTP_PROXY: "${HTTP_PROXY}"
HTTPS_PROXY: "${HTTPS_PROXY}"
environment:
- HTTP_PROXY=${HTTP_PROXY}
- HTTPS_PROXY=${HTTPS_PROXY}
- NO_PROXY=localhost,127.0.0.1,db,redis
depends_on:
- db
- redis
Beachten Sie NO_PROXY: Innerhalb des docker-compose-Netzwerks kommunizieren die Container untereinander über die Dienstnamen (db, redis) und nicht über localhost. Wenn Sie vergessen, die Dienstnamen in die Ausnahmen aufzunehmen, wird der gesamte interne Traffic zwischen den Containern ebenfalls über den externen Proxy geleitet, was die Latenz erhöht und die Verbindung unterbrechen kann, wenn der Proxy interne Protokolle wie PostgreSQL oder Redis nicht zulässt.
Fallstrick 5: CI/CD-Runner und das Leck von Proxy-Geheimnissen
In GitHub Actions, GitLab CI und Jenkins werden Proxy-Variablen häufig auf der Ebene des Runners oder der gesamten Organisation festgelegt, was zwei Probleme verursacht. Erstens, wenn die Proxy-URL Anmeldeinformationen im Klartext enthält, wird sie in das Build-Log aufgenommen, bei jedem Befehl, der die Umgebung ausgibt (env, printenv, Debug-Ausgaben in Skripten). Zweitens erben selbstgehostete Runner manchmal den systemweiten Proxy des Hosts, der nicht für den Traffic der CI-Jobs gedacht ist.
# .github/workflows/build.yml
jobs:
build:
runs-on: ubuntu-latest
env:
HTTP_PROXY: ${{ secrets.PROXY_URL }}
HTTPS_PROXY: ${{ secrets.PROXY_URL }}
NO_PROXY: "localhost,127.0.0.1,github.com"
steps:
- uses: actions/checkout@v4
- name: Testverbindung
run: curl -sS https://api.example.com/health
Sicherheitsregel: Speichern Sie die Proxy-URL nur in CI-Geheimnissen (GitHub Secrets, GitLab CI/CD Variables mit dem Flag "masked"), geben Sie niemals die Umgebungsvariablen vollständig in Debug-Logs aus und beschränken Sie NO_PROXY so, dass Anfragen an die CI-Plattform selbst (z.B. github.com, gitlab.com) nicht über den Proxy laufen — andernfalls kann der Build hängen bleiben, wenn der Proxy zu Spitzenzeiten nicht verfügbar ist.
Fallstrick 6: Sonderzeichen im Benutzernamen und Passwort des Proxys
Wenn das Passwort für den Proxy Zeichen wie @, :, / oder # enthält, zerlegt der URL-Parser die Zeichenfolge an den falschen Stellen. Ein klassischer Fehler: Das Passwort P@ssw0rd in der Zeichenfolge http://user:P@[email protected]:8080 wird vom Parser so gelesen, als ob der Host "[email protected]" und nicht "proxy.com" ist.
Die Lösung besteht darin, den Benutzernamen und das Passwort immer vor der Einfügung in die Variable URL-zu kodieren:
# Python: Passwort kodieren, bevor die Proxy-Zeichenfolge erstellt wird
import urllib.parse
user = "login"
password = "P@ss:w0rd/1"
encoded_password = urllib.parse.quote(password, safe="")
proxy_url = f"http://{user}:{encoded_password}@proxy.example.com:8080"
import os
os.environ["HTTPS_PROXY"] = proxy_url
# Node.js: ähnliche Erstellung über encodeURIComponent
const user = "login";
const password = "P@ss:w0rd/1";
const encoded = encodeURIComponent(password);
const proxyUrl = `http://${user}:${encoded}@proxy.example.com:8080`;
process.env.HTTPS_PROXY = proxyUrl;
Fallstrick 7: Anwendungen, die Umgebungsvariablen ignorieren
Nicht alle HTTP-Clients respektieren die systemweiten Proxy-Variablen. In Python liest die Bibliothek requests sie automatisch über requests.Session(), aber wenn Sie einmal explizit ein Dictionary proxies={} übergeben, überschreibt dies die Umgebungsvariablen und der Traffic wird direkt geleitet, selbst wenn HTTP_PROXY im System gesetzt ist.
import requests
# Leeres Dictionary proxies ÜBERGEHT die Umgebungsvariablen
response = requests.get("https://api.example.com", proxies={})
# Richtig — entweder den Proxy explizit übergeben oder proxies überhaupt nicht übergeben
proxies = {
"http": "http://user:[email protected]:8080",
"https": "http://user:[email protected]:8080",
}
response = requests.get("https://api.example.com", proxies=proxies)
In Node.js weiß der integrierte fetch überhaupt nichts von HTTP_PROXY ohne zusätzliche Bibliotheken. Sie müssen undici mit ProxyAgent oder den klassischen https-proxy-agent explizit einbinden:
const { ProxyAgent, setGlobalDispatcher } = require("undici");
const proxyUrl = process.env.HTTPS_PROXY;
if (proxyUrl) {
setGlobalDispatcher(new ProxyAgent(proxyUrl));
}
fetch("https://api.example.com/data")
.then(res => res.json())
.then(console.log);
Java benötigt ebenfalls separate systemweite Eigenschaften anstelle von Umgebungsvariablen: -Dhttp.proxyHost=proxy.example.com -Dhttp.proxyPort=8080 -Dhttps.proxyHost=proxy.example.com -Dhttps.proxyPort=8080. Umgebungsvariablen HTTP_PROXY werden in der JVM standardmäßig überhaupt nicht gelesen.
Wie man Proxy-Variablen richtig testet
Bevor Sie Zeit mit dem Debugging der Anwendung verschwenden, überprüfen Sie die Variable selbst und den Proxy isoliert vom Code. Das spart Stunden.
# 1. Überprüfen, ob die Variable tatsächlich in der aktuellen Sitzung gesetzt ist
env | grep -i proxy
# 2. Überprüfen der Funktionsfähigkeit des Proxys direkt über curl
curl -x "$HTTP_PROXY" -v https://api.ipify.org
# 3. Überprüfen, ob die Variable im Container angekommen ist
docker exec -it my_container env | grep -i proxy
# 4. Überprüfen, ob NO_PROXY den gewünschten Host tatsächlich ausschließt
curl -v --noproxy "internal.corp" https://service.internal.corp/health
Wenn die Aufgabe darin besteht, massenhaft Anfragen an externe APIs zu stellen, Marktplätze zu scrapen oder Geo-Targeting-Werbung aus einem CI-Pipeline zu testen, ist es wichtig, den richtigen Proxy-Typ für die Last auszuwählen. Für stabile, hochbelastete Szenarien in CI sind normalerweise Rechenzentrumsproxies ausreichend — sie sind schneller und günstiger als Residential-Proxies bei hohem Traffic von Builds und Tests. Wenn Ihre Pipeline jedoch das Verhalten einer Website von einer realen Benutzer-IP einer bestimmten Region überprüft, sollten Sie besser Residential-Proxies verwenden — sie verringern das Risiko einer Blockierung aufgrund einer "Rechenzentrums"-IP.
Checkliste vor dem Deployment
- Variablen sind sowohl in Groß- als auch in Kleinschreibung gleichzeitig gesetzt
- Benutzername und Passwort in der Proxy-URL sind über
urllib.parse.quoteoderencodeURIComponentkodiert - NO_PROXY enthält sowohl die Domain- als auch die Wildcard-Formatierung zur Kompatibilität zwischen den Tools
- In docker-compose enthält NO_PROXY die Dienstnamen aus dem Compose-Netzwerk und nicht nur localhost
- Für Docker build wurden build-args separat von runtime env über ENV im Dockerfile übergeben
- Secrets mit der Proxy-URL in CI sind maskiert und werden nicht in Debug-Logs ausgegeben
- Für Java-Anwendungen wird der Proxy über systemweite Eigenschaften -D und nicht nur über env gesetzt
- Der Schutz vor httpoxy wurde überprüft — der Proxy-Header vom Client wird auf der Ebene des Webservers auf null gesetzt
Fazit
Die Variablen HTTP_PROXY, HTTPS_PROXY und NO_PROXY sind auf den ersten Blick eine einfache Konvention, die in der Praxis an der Groß- und Kleinschreibung, der Syntax von Ausnahmen, den Grenzen zwischen Docker build und Docker run, der Vererbung von Umgebungen in docker-compose, dem Leck von Geheimnissen in CI-Logs und schließlich daran scheitert, dass nicht jede Bibliothek diese Variablen überhaupt liest. Überprüfen Sie jede Variable isoliert über curl und docker exec, bevor Sie nach einem Bug in der Geschäftslogik der Anwendung suchen.
Wenn Ihre Infrastruktur auf das Scraping externer Websites, das Testen von Geo-Targeting-Werbung oder das Umgehen von IP-Blockierungen aus einer CI-Pipeline angewiesen ist, ist es wichtig, nicht nur die Umgebungsvariablen korrekt zu setzen, sondern auch den geeigneten Proxy-Typ für die Aufgabe auszuwählen. Für das Testen von mobilem Traffic und Anwendungen, die das Verhalten von Mobilfunkanbietern überprüfen, sind mobile Proxies geeignet — sie bieten IPs echter Mobilfunknetze und geraten seltener in die Filter von Anti-Fraud-Systemen.