localproxy — Anleitung & Tipps
localproxy ist ein schlanker lokaler HTTP-Proxy für Browser-basierte Online-Tools — Download, Start, Konfiguration und Endpunkte, um externe URLs ohne CORS-Probleme abzurufen.
localproxy ist ein schlanker, sicherer lokaler HTTP-Proxy für Browser-basierte Online-Tools. Er bindet ausschließlich an 127.0.0.1 und erlaubt deinen Web-Anwendungen, externe URLs abzurufen — ohne an den CORS-Beschränkungen des Browsers zu scheitern.
Anleitung
Voraussetzungen
- Eine vorgefertigte Binary für deine Plattform (Linux, macOS, Windows, FreeBSD, OpenBSD, NetBSD — jeweils gängige Architekturen)
- Alternativ für den Build aus dem Quellcode: Go 1.26+
Binary herunterladen
Vorgefertigte Binaries für alle Plattformen liegen im Repository unter Releases (z. B. localproxy-linux-amd64, localproxy-macos-apple-silicon, localproxy-windows-amd64.exe).
Ausführbar machen (macOS / Linux)
chmod +x ./localproxy-macos-apple-silicon
# macOS: Gatekeeper-Warnung der unsignierten Binary aufheben:
xattr -dr com.apple.quarantine ./localproxy-macos-apple-siliconStarten
Im einfachsten Fall ohne weitere Konfiguration:
./localproxyBeim Start zeigt ein Banner die wichtigen Werte an: die Adresse (http://127.0.0.1:PORT — ohne --port ein zufälliger freier Port), das bei jedem Start neu erzeugte Token, die verwendeten DNS-Server und die zugelassenen Origins.
Konfiguration
--port int TCP-Port (0 = zufälliger freier Port) [Standard: 0]
--origin string Erlaubte Origins, kommagetrennt
--timeout int Timeout für Upstream-Anfragen in Sekunden [Standard: 30]
--max-mb int64 Maximale Antwortgröße in MB (0 = unbegrenzt) [Standard: 50]
--dns string DNS-Server, kommagetrennt, oder "system" für OS-DNS [Standard: "1.1.1.1,8.8.8.8"]
--allow-any-host Nicht-lokale Host-Header akzeptieren (nicht empfohlen)
--version Versionsinformation ausgeben und beenden--port, --timeout und --max-mb werden beim Start validiert — ein Wert außerhalb des zulässigen Bereichs bricht mit einer Meldung ab, statt später verwirrend fehlzuschlagen.
Beispiele:
# Produktion: nur ein bestimmtes Tool zulassen
./localproxy --origin https://deintool.example.com
# Eigene DNS-Server (Quad9 + Cloudflare)
./localproxy --dns "9.9.9.9,1.1.1.1"
# DNS-Resolver des Betriebssystems verwenden
./localproxy --dns systemStandardmäßig löst localproxy Namen über Cloudflare (1.1.1.1) und Google (8.8.8.8) auf — unabhängig von der DNS-Konfiguration des Hosts, was über Maschinen hinweg konsistente Ergebnisse liefert. Fehlt beim --dns-Wert der Port, wird :53 automatisch ergänzt.
--allow-any-host
Standardmäßig beantwortet localproxy nur Anfragen, deren Host-Header das Loopback-Interface benennt (127.0.0.0/8, localhost oder [::1], mit beliebigem Port). Das blockiert DNS-Rebinding: Ohne die Prüfung könnte eine Webseite einen von ihr kontrollierten Hostnamen auf 127.0.0.1 zeigen lassen und den Proxy aus deinem Browser heraus ansprechen.
--allow-any-host brauchst du nur, wenn du den Proxy unter einem anderen lokalen Namen erreichst — etwa über einen Container-Alias. Greif lieber zu --origin, statt die Prüfung abzuschalten.
Endpunkte
| Endpunkt | Auth | Beschreibung |
|---|---|---|
GET/POST/HEAD /proxy?url=... | ja (X-Proxy-Token) | Anfrage an die Ziel-URL weiterleiten (Body-Streaming, Metadaten-Header) |
GET /inspect?url=... | ja | Verbindungs-Metadaten als JSON (SSL, Timing, IP, Header, http3); &body=1 inkl. Body |
GET /page?url=... | ja | Vollständige Seitenanalyse: Redirect-Kette + Body + SSL + Timing + http3 in einem JSON |
| `OPTIONS /proxy | /inspect | /page` |
GET /ping | nein | Health-Check (gibt localproxy ok zurück) |
GET /version | nein | Versionsinfo als JSON |
Einbindung im Tool
- Nutzer gibt Adresse (
http://127.0.0.1:PORT) und Token im Online-Tool ein. - Das Tool prüft die Verbindung über
/ping. - Alle weiteren Anfragen laufen über
/proxy?url=...mit dem HeaderX-Proxy-Token. Beispiel:
const response = await fetch(
`${PROXY_BASE}/proxy?url=${encodeURIComponent(targetUrl)}`,
{ headers: { "X-Proxy-Token": PROXY_TOKEN } }
);Jede /proxy-Antwort liefert zusätzlich Upstream-Metadaten als Header: X-Upstream-Protocol, X-Upstream-IP, X-Upstream-Timing (z. B. dns=12;tcp=45;ssl=23;ttfb=156;total=234), X-Upstream-Content-Encoding, X-Upstream-Content-Length und — nur wenn der Body die --max-mb-Grenze gerissen hat und abgeschnitten wurde — X-Upstream-Truncated: 1. Alle Upstream-Header sind per JavaScript lesbar.
Fehler sind auswertbar: Alle 4xx-Antworten tragen CORS-Header und einen JSON-Body mit maschinenlesbarem Code (bad_request, forbidden, blocked_target, method_not_allowed) — ein falsches Token ist damit von einem nicht erreichbaren Proxy unterscheidbar. Nur abgelehnte Origins bekommen bewusst ein nacktes 403 ohne CORS-Header.
Diese Header gehen nicht ans Ziel: Cookie, Cookie2, Authorization, Proxy-Authorization, Origin, Referer und Sec-Fetch-* werden nie weitergereicht, damit Anmeldedaten und Identitätssignale des Aufrufers nicht beim Zielserver landen. Header, die du bewusst setzt — Accept, User-Agent, Accept-Language, eigene X-* — passieren weiterhin.
Tipps & Tricks
- In Produktion Origins einschränken: Ohne
--originsind alle Origins erlaubt. Für den produktiven Einsatz immer das konkrete Tool per--origin https://deintool.example.comfreigeben. - HTTPS-Seite ruft HTTP-localhost auf — kein Problem: Browser behandeln
http://127.0.0.1als „potentially trustworthy origin" (W3C Secure Contexts). Anfragen von einer HTTPS-Seite an den lokalen Proxy werden nicht als Mixed Content blockiert; Chromes Private-Network-Access-Preflight wird automatisch beantwortet. /pagespart Roundtrips: Der Endpunkt verfolgt die Redirect-Kette (bis zu 20 Hops), lädt den Body, inspiziert SSL und misst Timings in einem einzigen JSON — er ersetzt mehrere/proxy- und/inspect-Aufrufe. Seit 1.1.0 leitet er sein gesamtes Zeitbudget aus dem Request-Kontext des Clients ab: Bricht der Client ab, endet auch die Arbeit am Server. Vorher liefen Redirect-Hops und Encoding-Proben mitcontext.Background(), sodass 20 langsame Redirects einen Handler minutenlang am Leben halten konnten.- SSL-Details, die der Browser nicht hergibt:
/inspectliefert Zertifikatsinformationen (Version, Aussteller, Gültigkeit, SANs, Chain — inklusive IP-SANs), die über die Fetch-API nicht zugänglich sind. Ohnebody=1wird upstream nur ein HEAD-Request gesendet (schneller, kein Body-Download). - HTTP/3 erkennen, ohne HTTP/3 zu sprechen:
/inspectund/pageliefern seit Version 1.1.0 ein Feldhttp3. Es beantwortet „unterstützt dieses Ziel HTTP/3?" — nicht „lief diese Anfrage über HTTP/3?". Die Antwort stammt aus demAlt-Svc-Feld der Zielantwort (RFC 7838, ALPN-Tokenh3nach RFC 9114); genau so entdecken Browser HTTP/3 auch, nämlich über TCP. Es ist also kein QUIC-Stack im Spiel. Entwurfs-Token wieh3-29werden ignoriert, das roheAlt-Svc-Feld bleibt in derheaders-Map verfügbar.protocolsagt weiterhin, worüber diese eine Anfrage tatsächlich lief — die Kombination{"protocol": "HTTP/2.0", "http3": true}ist der Normalfall, kein Widerspruch. - Sicherheits-Defaults: 48-Zeichen-Session-Token bei jedem Start neu, SSRF-Schutz, TLS-Upstream-Verifikation immer aktiv, Redirects werden nicht automatisch gefolgt (der Client entscheidet), Antwortgröße per
--max-mbgedeckelt, Token-Vergleich in konstanter Zeit,X-Content-Type-Options: nosniffauf allen selbst erzeugten Antworten. - SSRF-Schutz greift erst im Dialer: Seit 1.1.0 wird die Zieladresse nach der DNS-Auflösung und vor dem Verbindungsaufbau geprüft. Die frühere Vorab-Prüfung löste den Namen getrennt vom HTTP-Client auf — eine vom Angreifer kontrollierte Zone mit kurzer TTL konnte der Prüfung eine öffentliche und der Verbindung eine Loopback-Adresse liefern. Die Vorab-Prüfung bleibt als schneller Abbruchpfad erhalten und ist auf 5 s begrenzt. Die Sperrliste deckt jetzt auch reservierten IPv4-Raum (
0/8,192.0.0.0/24,198.18/15, Multicast,240/4) und die IPv6-Formen mit eingebetteter IPv4-Adresse ab — IPv4-mapped, NAT64 (64:ff9b::/96) und 6to4 (2002::/16).64:ff9b::7f00:1erreicht in einem NAT64-Netz127.0.0.1und war einer rein v4-basierten Prüfung vorher unsichtbar. - Nebenläufigkeit & Kompression: Der Proxy bearbeitet Anfragen parallel (eine Goroutine pro Verbindung); jede Upstream-Anfrage nutzt eine frische Verbindung für genaues Timing. Brotli, gzip und deflate werden bei
/proxytransparent durchgereicht — die Dekompression übernimmt der Browser. Bei/inspectmitbody=1dekomprimiert der Proxy selbst;Content-Encoding: deflateversteht seit 1.1.0 sowohl zlib-verpackte Daten (RFC 1950) als auch rohes DEFLATE (RFC 1951) — vorher kam die zlib-Variante, die ein großer Teil der Server sendet, leer oder beschädigt zurück. - Aus dem Quellcode bauen: Mit Go 1.26+ via
go build -o localproxy .(die Releases werden mit Go 1.26.5 gebaut); Cross-Compiles überGOOS/GOARCH(z. B.GOOS=windows GOARCH=amd64 CGO_ENABLED=0 go build -ldflags="-s -w" -trimpath -o localproxy.exe .). Ein Git-Tag (git tag v1.1.0 && git push origin v1.1.0) stößt über GitHub Actions automatisch plattformübergreifende Release-Builds an. - Tests & Linting:
go test -race ./...für den Race-Detector; das Projekt wird mit staticcheck gelintet, dazu kommengovulncheckund einegofmt-Prüfung (CI blockt den Release bei jedem Befund). Seit 1.1.0 läuft die CI bei jedem Push und Pull Request, nicht mehr nur auf Tags — ein kaputter Test fällt damit nicht erst zum Release auf. staticcheck ist reines Dev-Tooling und landet nie in der Binary.