localproxy — Guide & Tips
localproxy is a lightweight local HTTP proxy for browser-based tools — download, start, configuration and endpoints to fetch URLs without CORS.
localproxy is a lightweight, secure local HTTP proxy for browser-based online tools. It binds exclusively to 127.0.0.1 and lets your web applications fetch external URLs — safely bypassing the browser's CORS restrictions.
Guide
Requirements
- A pre-built binary for your platform (Linux, macOS, Windows, FreeBSD, OpenBSD, NetBSD — common architectures each)
- Alternatively, to build from source: Go 1.26+
Download the binary
Pre-built binaries for all platforms are available under Releases in the repository (e.g. localproxy-linux-amd64, localproxy-macos-apple-silicon, localproxy-windows-amd64.exe).
Make it executable (macOS / Linux)
chmod +x ./localproxy-macos-apple-silicon
# macOS: clear the Gatekeeper warning for the unsigned binary:
xattr -dr com.apple.quarantine ./localproxy-macos-apple-siliconStart it
In the simplest case, with no further configuration:
./localproxyOn startup a banner shows the key values: the address (http://127.0.0.1:PORT — a random free port without --port), the token regenerated on every start, the DNS servers in use and the allowed origins.
Configuration
--port int TCP port to listen on (0 = random free port) [default: 0]
--origin string Allowed origins, comma-separated
--timeout int Upstream request timeout in seconds [default: 30]
--max-mb int64 Maximum upstream response size in MB (0 = unlimited) [default: 50]
--dns string DNS servers, comma-separated, or "system" for OS DNS [default: "1.1.1.1,8.8.8.8"]
--version Print version information and exitExamples:
# Production: allow only one specific tool
./localproxy --origin https://yourtool.example.com
# Custom DNS servers (Quad9 + Cloudflare)
./localproxy --dns "9.9.9.9,1.1.1.1"
# Use the operating system's DNS resolver
./localproxy --dns systemBy default localproxy resolves names via Cloudflare (1.1.1.1) and Google (8.8.8.8) — independent of the host's DNS configuration, which keeps results consistent across machines. If the --dns value omits a port, :53 is appended automatically.
Endpoints
| Endpoint | Auth | Description |
|---|---|---|
GET/POST/HEAD /proxy?url=... | yes (X-Proxy-Token) | Forward the request to the target URL (body streaming, metadata headers) |
GET /inspect?url=... | yes | Connection metadata as JSON (SSL, timing, IP, headers); &body=1 includes the body |
GET /page?url=... | yes | Full page analysis: redirect chain + body + SSL + timing in one JSON |
| `OPTIONS /proxy | /inspect | /page` |
GET /ping | no | Health check (returns localproxy ok) |
GET /version | no | Version info as JSON |
Integrating it in a tool
- The user enters the address (
http://127.0.0.1:PORT) and token in the online tool. - The tool verifies the connection via
/ping. - All further requests go through
/proxy?url=...with theX-Proxy-Tokenheader. Example:
const response = await fetch(
`${PROXY_BASE}/proxy?url=${encodeURIComponent(targetUrl)}`,
{ headers: { "X-Proxy-Token": PROXY_TOKEN } }
);Every /proxy response also carries upstream metadata as headers: X-Upstream-Protocol, X-Upstream-IP, X-Upstream-Timing (e.g. dns=12;tcp=45;ssl=23;ttfb=156;total=234), X-Upstream-Content-Encoding and X-Upstream-Content-Length. All upstream headers are readable from JavaScript.
Tips & Tricks
- Restrict origins in production: Without
--originall origins are allowed. For production use, always allow the specific tool via--origin https://yourtool.example.com. - HTTPS page calling HTTP localhost — no problem: Browsers treat
http://127.0.0.1as a "potentially trustworthy origin" (W3C Secure Contexts). Requests from an HTTPS page to the local proxy are not blocked as mixed content; Chrome's Private Network Access preflight is answered automatically. /pagesaves round-trips: The endpoint traces the redirect chain (up to 20 hops), downloads the body, inspects SSL and measures timing in a single JSON — replacing multiple/proxyand/inspectcalls.- SSL details the browser won't give you:
/inspectreturns certificate information (version, issuer, validity, SANs, chain) that the Fetch API can't access. Withoutbody=1it sends only a HEAD request upstream (faster, no body download). - Security defaults: 48-character session token regenerated on each start, SSRF protection (target hosts are resolved and checked against private/loopback CIDRs), upstream TLS verification always on, redirects are not followed automatically (the client decides), response size capped via
--max-mb. - Concurrency & compression: The proxy handles requests concurrently (one goroutine per connection); each upstream request uses a fresh connection for accurate timing. Brotli, gzip and deflate pass through transparently on
/proxy— the browser handles decompression. - Build from source: With Go 1.26+ via
go build -o localproxy .; cross-compile withGOOS/GOARCH(e.g.GOOS=windows GOARCH=amd64 CGO_ENABLED=0 go build -ldflags="-s -w" -trimpath -o localproxy.exe .). A Git tag (git tag v1.0.5 && git push origin v1.0.5) triggers cross-platform release builds automatically via GitHub Actions. - Tests & linting:
go test -race ./...for the race detector; the project is linted with staticcheck (CI blocks the release on any finding). staticcheck is dev tooling only and never ships with the binary.