run-proxy-server
v1.1.0
Published
Simple tool to proxy HTTP/HTTPS requests via a local Node.js server with built-in caching
Maintainers
Readme
run-proxy-server
Simple CLI tool to run a local HTTP/HTTPS proxy server with built-in caching, powered by vanilla Node.js.

Give a ⭐️ if this project helped you!
Features
- 🚀 Create a local proxy server (HTTP or HTTPS)
- ⚡ Cache proxied responses to avoid redundant network requests
- 💾 Cache stored in
~/.cache/run-proxy-server, keyed by a SHA-256 hash of the full URL - 🧰 Supports a denylist to exclude specific URLs or patterns from caching
- 🎯 Denylist patterns support wildcards (
*) - 🔒 Only
GETresponses are cached;Set-Cookie,Cache-Control: private/no-store, authorized requests and 5xx are always fetched fresh - 🏠 Listens on loopback (
127.0.0.1) only unless you opt in with--host
Quick Start
Install
npm install -g run-proxy-serverOr run without installing:
npx run-proxy-server https://example.com --port 8000One-time HTTPS setup
If you plan to proxy an HTTPS target, run this once first:
run-proxy-server --setup-https
# or
npx run-proxy-server --setup-httpsThis creates key.pem and cert.pem in $XDG_CONFIG_HOME/run-proxy-server/certs (falling back to ~/.config/run-proxy-server/certs). The certificates belong to the user, not to the package, so a global reinstall does not wipe them.
HTTP
run-proxy-server https://example.com --port 8000Then make requests to the proxy:
curl http://localhost:8000/HTTPS
After the one-time HTTPS setup above, start the proxy pointing to an HTTPS target:
run-proxy-server https://example.com --port 8443If you prefer manual certificate creation instead of --setup-https, you can run:
mkdir -p ~/.config/run-proxy-server/certs
openssl req -x509 -newkey rsa:2048 -keyout ~/.config/run-proxy-server/certs/key.pem -out ~/.config/run-proxy-server/certs/cert.pem -days 365 -nodes -subj "/CN=localhost"Note: Browsers will show a security warning for self-signed certificates - this is expected in local development. For production, use a certificate from a trusted CA (e.g. Let's Encrypt).
Options
| Argument/Option | Required | Default | Description |
| --- | --- | --- | --- |
| URL | yes | - | Target URL to proxy requests to |
| --host | no | localhost | Interface to listen on (0.0.0.0 exposes the proxy to the network) |
| --port | no | 8000 | Port for the local proxy server |
| --denylist | no | - | Comma-separated URL patterns to exclude from cache |
| --no-cache | no | false | Disable cache reads and writes for this process |
| --clear-cache | no | false | Remove all cached responses and exit |
| --setup-https | no | false | Generate local HTTPS certificates and exit |
Examples
Proxy an API with caching
run-proxy-server https://api.github.com --port 8000
# First request - proxied and cached
curl http://localhost:8000/users/octocat
# Second request - served from cache (no network call)
curl http://localhost:8000/users/octocatExclude dynamic endpoints from cache
run-proxy-server https://example.com --port 8000 --denylist "*/api/*,*.json"
# API calls are never cached (always fresh)
curl http://localhost:8000/api/users
# Static assets are cached
curl http://localhost:8000/index.htmlExpose the proxy to other devices
By default the proxy listens on loopback (127.0.0.1) only, because it replays cached responses to every client. Opt in explicitly when you need it on the network:
run-proxy-server https://example.com --host 0.0.0.0 --port 8000Disable cache entirely for a run
run-proxy-server https://example.com --port 8000 --no-cache
# Every request is fetched from upstream (cache is bypassed)
curl http://localhost:8000/Denylist pattern syntax
Patterns are matched against the full URL. * matches any characters.
| Pattern | Matches |
| --------------------------- | ------------------------------------- |
| *.json | https://example.com/data.json |
| */api/* | https://example.com/api/users |
| */admin/* | https://example.com/admin/dashboard |
| https://cdn.example.com/* | https://cdn.example.com/image.png |
Multiple patterns are separated by commas:
--denylist "*/api/*,*.json,*/admin/*"Cache
Responses live in $XDG_CACHE_HOME/run-proxy-server, falling back to ~/.cache/run-proxy-server. The cache belongs to the user, not to the package - installed globally, the package directory sits inside node_modules, which is read-only in many setups and wiped on every reinstall.
- First request: proxied to the target, response saved to the cache
- Subsequent requests: served directly from the cache
- Only
GET:HEAD,POSTand other methods always go upstream and are never stored - Never stored: responses with
Set-CookieorCache-Control: private/no-store, responses to requests carryingAuthorization, and 5xx errors - Denylisted URLs: always fetched fresh, never cached
--no-cachemode: cache is fully disabled (no reads and no writes)- Expiry: entries are valid for 365 days; set
CACHE_TTL_HOURSto change the window, or0to keep them forever
Each entry is a JSON file named after the first 32 hex characters of the SHA-256 hash of the URL. Binary bodies are stored base64-encoded.
# Keep responses for an hour instead of a year
CACHE_TTL_HOURS=1 run-proxy-server https://example.comClear the cache at any time:
run-proxy-server --clear-cache
# or
npx run-proxy-server --clear-cacheDevelopment
Run with auto-reload on file changes:
npm run dev -- https://example.com --port 8000Testing
npm testTests cover cache operations, denylist pattern matching, request handling (cache rules, hop-by-hop headers, binary bodies, 502/504 mapping) and certificate lookup across Node.js 20, 22, and 24.
Format and lint before committing:
npm run format:check
npm run lint🤝 Contributing
Contributions, issues and feature requests are welcome! Feel free to check issues page.
License
The MIT License @ 2026
