Local reverse proxy for mapping external websites to a local domain or IP while rewriting requests and responses back and forth.
Features:
- accepts requests on a local IP/port or local domain;
- maps them to an external
hostnameandport; - rewrites domains and ports back to local values in response headers;
- rewrites external domains inside
HTML,JS,CSS,JSON,SVG,XML, and other textual responses; - supports subdomains with rules like
api.example.com -> api.local.test; - works without editing
/etc/hostsby using wildcard DNS names like192.168.1.91.sslip.io; - softens CORS for local debugging by handling
OPTIONSpreflight, reflecting the localOrigin, and rewritingOrigin/Refererback to the upstream domain; - preserves text encodings including
utf-8,latin1, andwindows-1251; - supports local HTTPS with its own CA and per-host certificates;
- proxies WebSocket upgrade requests using the same host-mapping rules.
cd /path/to/local-domain-proxy
TARGET_ORIGIN=https://example.com LISTEN_PORT=8080 npm startCLI version:
cd /path/to/local-domain-proxy
node src/index.js --target https://example.com --port 8080Minimal CLI version with defaults:
cd /path/to/local-domain-proxy
node src/index.js --target https://example.comHTTPS mode:
cd /path/to/local-domain-proxy
TARGET_ORIGIN=https://example.com HTTPS_ENABLED=1 HTTPS_PORT=8443 PUBLIC_PORT=8443 npm startCLI version:
cd /path/to/local-domain-proxy
node src/index.js --target https://example.com --https --https-port 8443 --public-port 8443Minimal HTTPS CLI version with defaults:
cd /path/to/local-domain-proxy
node src/index.js --target https://example.com --httpsUse Burp for outgoing traffic:
cd /path/to/local-domain-proxy
node src/index.js --target https://example.com --https --burp-proxy 127.0.0.1:8080The short --burp-proxy flag automatically expands to:
--upstream-proxy http://127.0.0.1:8080--insecure-upstream-tls
If you want to use a generic upstream HTTP proxy without automatically disabling TLS verification, use --upstream-proxy.
If you want Burp to see the local request before host rewriting, add:
cd /path/to/local-domain-proxy
node src/index.js --target https://example.com --https --burp-proxy 127.0.0.1:8080 --burp-before-rewriteIn this mode, the proxy first sends the local URL through Burp, then routes the request back into the reverse proxy for rewriting and upstream delivery. Burp will therefore show the local target instead of the original domain.
The second pass after Burp is marked as an internal re-entry and is not sent through Burp again, so you do not get duplicate "before" and "after" entries in Burp history.
This is useful when you want Burp to see the local Host, local URL, and local HTTPS session before the reverse proxy rewrites the request to the external origin.
If LOCAL_DOMAIN is not set, the proxy automatically uses:
192.168.1.91.sslip.ioifPUBLIC_HOSTlooks like an IPv4 address;- otherwise
PUBLIC_HOSTitself.
If PUBLIC_HOST is not set, the proxy auto-detects the first suitable external IPv4 and prefers private LAN addresses such as 192.168.x.x or 10.x.x.x.
Environment variables:
TARGET_ORIGIN- upstream origin, for examplehttps://example.comorhttps://example.com:8443LOCAL_DOMAIN- local rewrite domain, for examplelocal.test; if unset it is built automatically fromPUBLIC_HOSTTARGET_CONNECT_HOST- optional real TCP host to connect to if the logical upstream domain should not or cannot be resolved directlyTARGET_CONNECT_PORT- optional real TCP port for upstream TCP connectionsUPSTREAM_PROXY- HTTP proxy for outgoing traffic, for examplehttp://127.0.0.1:8080; useful for Burp SuiteUPSTREAM_PROXY_STAGE- upstream proxy mode:upstreamorpre-rewrite; inpre-rewritemode Burp sees the local URL first, then the request returns to the reverse proxy and only after that goes to the external originADDITIONAL_TARGET_DOMAINS- additional root domains, comma-separated, for examplestatic.example.net,auth.example.orgLISTEN_HOST- interface to listen on, default0.0.0.0LISTEN_PORT- local HTTP listener port, default8080HTTPS_ENABLED- enables the local HTTPS listener and certificate generation when set to1ortrueHTTPS_PORT- HTTPS listener port, default8443PUBLIC_HOST- host used in rewritten responses; defaults to the first detected external IPv4PUBLIC_PORT- port used in rewritten responses; defaults toLISTEN_PORTPUBLIC_PROTOCOL- protocol used when rewriting links; defaults tohttps:whenHTTPS_ENABLED, otherwisehttp:WILDCARD_DNS_SUFFIX- wildcard DNS suffix, defaultsslip.ioTLS_DIR- directory for the local CA and issued certificates, default.local-tlsin the project rootUPSTREAM_TLS_DISABLE_SNI- if set to1, disables SNI for upstream TLS connections; useful for hosts that fail withtlsv1 unrecognized nameUPSTREAM_TLS_INSECURE- if set to1, disables upstream TLS certificate verification; useful when intercepting HTTPS through BurpUPSTREAM_TLS_SERVERNAME- manually overrides the upstream TLSservernameDEBUG_PROXY- if set to1, prints detailed routing, upstream host selection, and TLS/HTTP errorsUSE_BROWSER_HEADERS- if set to1or left unset, injects browser-like upstream headers when the client did not provide themBROWSER_USER_AGENT,BROWSER_ACCEPT,BROWSER_ACCEPT_LANGUAGE,BROWSER_SEC_FETCH_SITE,BROWSER_SEC_FETCH_MODE,BROWSER_SEC_FETCH_DEST,BROWSER_UPGRADE_INSECURE_REQUESTS,BROWSER_SEC_CH_UA,BROWSER_SEC_CH_UA_MOBILE,BROWSER_SEC_CH_UA_PLATFORM- override the default browser-like headers
- A request to
http://127.0.0.1:8080/pathgoes to the main upstream fromTARGET_ORIGIN. - A request to
http://192.168.1.91.sslip.io:8080/pathalso goes to the main upstream. - A request to
http://api.192.168.1.91.sslip.io:8080/pathgoes toapi.example.com. - If additional domains are enabled, separate local suffixes are created for them:
www.static.example.net -> www.static-example-net.192.168.1.91.sslip.iologin.auth.example.org -> login.auth-example-org.192.168.1.91.sslip.io- In
Location,Set-Cookie,Access-Control-Allow-Origin, and other textual headers, domains are rewritten back to the local form. - Textual response bodies also have absolute URLs, authorities, and hostnames rewritten.
By default this is not a local DNS server. The proxy simply uses a public wildcard DNS service such as sslip.io, where:
192.168.1.91.sslip.io -> 192.168.1.91api.192.168.1.91.sslip.io -> 192.168.1.91
Because of that, the browser can resolve both the main local domain and its subdomains without a custom DNS server or /etc/hosts entries.
When HTTPS_ENABLED=1, the proxy:
- generates a local CA;
- generates a certificate for each incoming
Host; - uses
SNIso thatapi.<local-domain>andwww.<local-domain>receive separate certificates; - automatically re-issues older local certificates if they were created without the browser-required TLS extensions.
Files are stored in .local-tls.
To remove browser certificate warnings, import the CA from:
/path/to/local-domain-proxy/.local-tls/local-proxy-ca.cert.pem
Example for macOS:
sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain /path/to/local-domain-proxy/.local-tls/local-proxy-ca.cert.pemExample for Windows (cmd as Administrator):
certutil -addstore -f Root "C:\path\to\local-proxy-ca.cert.pem"Example for Windows PowerShell as Administrator:
Import-Certificate -FilePath "C:\path\to\local-proxy-ca.cert.pem" -CertStoreLocation Cert:\LocalMachine\RootIf you do not want to use sslip.io, you can still add entries to /etc/hosts, for example:
127.0.0.1 local.test api.local.test static.local.test
Then run the proxy with LOCAL_DOMAIN=local.test.
- Body rewriting is only applied to textual content types.
- Binary responses such as images, fonts, or archives are proxied unchanged.
- For WebSocket, the proxy rewrites
Host,Origin,Referer, and upgrade headers, but does not modify the frames inside the connection.
Локальная reverse proxy, которая позволяет открывать внешний сайт как локальный домен или IP и на лету переписывать запросы и ответы в обе стороны.
Возможности:
- принимает запросы на локальный IP/порт или локальный домен;
- автоматически подставляет внешний
hostnameиportдля upstream-запроса; - переписывает домен и порт обратно на локальные в заголовках ответа;
- заменяет внешний домен в
HTML,JS,CSS,JSON,SVG,XMLи других текстовых ответах; - поддерживает сабдомены по правилу
api.example.com -> api.local.test; - умеет работать без правки
/etc/hosts, используя wildcard DNS вида192.168.1.91.sslip.io; - смягчает CORS для локальной отладки: обрабатывает preflight
OPTIONS, отражает локальныйOriginвAccess-Control-Allow-Originи переписываетOrigin/Refererобратно во внешний домен для upstream; - сохраняет текстовые кодировки ответа, включая
utf-8,latin1иwindows-1251; - умеет поднимать локальный HTTPS с собственным CA и сертификатами по
Host; - проксирует WebSocket upgrade-запросы по тем же правилам маппинга доменов.
cd /path/to/local-domain-proxy
TARGET_ORIGIN=https://example.com LISTEN_PORT=8080 npm startCLI-вариант:
cd /path/to/local-domain-proxy
node src/index.js --target https://example.com --port 8080Минимальный CLI-вариант с дефолтами:
cd /path/to/local-domain-proxy
node src/index.js --target https://example.comHTTPS-режим:
cd /path/to/local-domain-proxy
TARGET_ORIGIN=https://example.com HTTPS_ENABLED=1 HTTPS_PORT=8443 PUBLIC_PORT=8443 npm startCLI-вариант:
cd /path/to/local-domain-proxy
node src/index.js --target https://example.com --https --https-port 8443 --public-port 8443Минимальный HTTPS CLI-вариант с дефолтами:
cd /path/to/local-domain-proxy
node src/index.js --target https://example.com --httpsЧерез Burp для исходящего трафика:
cd /path/to/local-domain-proxy
node src/index.js --target https://example.com --https --burp-proxy 127.0.0.1:8080Короткий флаг --burp-proxy автоматически разворачивается в:
--upstream-proxy http://127.0.0.1:8080--insecure-upstream-tls
Если нужен не Burp, а любой другой upstream HTTP proxy без автоотключения TLS-проверки, используй --upstream-proxy.
Если хочешь, чтобы Burp видела локальный запрос до подмены на оригинальный домен, добавь:
cd /path/to/local-domain-proxy
node src/index.js --target https://example.com --https --burp-proxy 127.0.0.1:8080 --burp-before-rewriteВ этом режиме прокси сначала отправляет через Burp именно локальный URL, а уже потом возвращает запрос обратно в reverse proxy на переписывание и выход наружу. Поэтому в Burp target будет локальным, а не оригинальным.
Повторный проход после возврата из Burp помечается как внутренний re-entry и уже не отправляется в Burp повторно, чтобы в истории не было дублей "до" и "после" переписывания.
Это удобно, если ты хочешь в Burp видеть локальный Host, локальный URL и локальную HTTPS-сессию до того, как reverse proxy подменит всё на внешний origin.
Если LOCAL_DOMAIN не задан, прокси автоматически возьмет:
192.168.1.91.sslip.io, еслиPUBLIC_HOSTвыглядит как IPv4;- иначе сам
PUBLIC_HOST.
Если PUBLIC_HOST не задан, прокси сама выберет первый подходящий внешний IPv4, предпочитая приватный LAN-адрес вроде 192.168.x.x или 10.x.x.x.
Переменные окружения:
TARGET_ORIGIN- внешний origin, напримерhttps://example.comилиhttps://example.com:8443LOCAL_DOMAIN- локальный домен для переписывания, напримерlocal.test; если не задан, будет автоматически собран изPUBLIC_HOSTTARGET_CONNECT_HOST- необязательный реальный адрес для TCP-подключения к upstream, если логический внешний домен резолвить не нужно или нельзяTARGET_CONNECT_PORT- необязательный порт для TCP-подключения к upstreamUPSTREAM_PROXY- HTTP proxy для исходящего трафика, напримерhttp://127.0.0.1:8080; удобно для Burp SuiteUPSTREAM_PROXY_STAGE- режим upstream proxy:upstreamилиpre-rewrite; во втором режиме Burp видит сначала локальный URL, после чего запрос возвращается в reverse proxy и уже затем уходит на внешний originADDITIONAL_TARGET_DOMAINS- дополнительные корневые домены через запятую, которые тоже нужно переписывать и проксировать, напримерstatic.example.net,auth.example.orgLISTEN_HOST- интерфейс, на котором слушает прокси, по умолчанию0.0.0.0LISTEN_PORT- локальный порт листнера, по умолчанию8080HTTPS_ENABLED- включает локальный HTTPS listener и генерацию сертификатов, если равно1илиtrueHTTPS_PORT- порт HTTPS listener, по умолчанию8443PUBLIC_HOST- хост, который будет подставляться в ответах вместо внешнего домена; по умолчанию берется первый внешний IPv4PUBLIC_PORT- порт, который будет подставляться в ответах; по умолчанию совпадает сLISTEN_PORTPUBLIC_PROTOCOL- протокол для переписывания ссылок в ответах; по умолчаниюhttps:приHTTPS_ENABLED, иначеhttp:WILDCARD_DNS_SUFFIX- суффикс wildcard DNS, по умолчаниюsslip.ioTLS_DIR- каталог для локального CA и выданных сертификатов, по умолчанию.local-tlsв корне проектаUPSTREAM_TLS_DISABLE_SNI- если равно1, прокси отключает SNI при TLS-подключении к upstream; полезно для хостов, которые отвечаютtlsv1 unrecognized nameUPSTREAM_TLS_INSECURE- если равно1, отключает проверку TLS-сертификата upstream; полезно при перехвате HTTPS через BurpUPSTREAM_TLS_SERVERNAME- вручную задаетservernameдля upstream TLS вместо вычисленного hostnameDEBUG_PROXY- если равно1, прокси печатает подробный маршрут запроса, выбранный upstream host и ошибки TLS/HTTPUSE_BROWSER_HEADERS- если равно1или не задано, прокси подставляет браузероподобные заголовки для upstream, если клиент их не прислалBROWSER_USER_AGENT,BROWSER_ACCEPT,BROWSER_ACCEPT_LANGUAGE,BROWSER_SEC_FETCH_SITE,BROWSER_SEC_FETCH_MODE,BROWSER_SEC_FETCH_DEST,BROWSER_UPGRADE_INSECURE_REQUESTS,BROWSER_SEC_CH_UA,BROWSER_SEC_CH_UA_MOBILE,BROWSER_SEC_CH_UA_PLATFORM- позволяют переопределить браузерные заголовки по умолчанию
- Запрос на
http://127.0.0.1:8080/pathуйдет на основной upstream изTARGET_ORIGIN. - Запрос на
http://192.168.1.91.sslip.io:8080/pathтоже уйдет на основной upstream. - Запрос на
http://api.192.168.1.91.sslip.io:8080/pathуйдет наapi.example.com. - Если включены дополнительные домены, то для них создаются свои локальные суффиксы:
www.static.example.net -> www.static-example-net.192.168.1.91.sslip.iologin.auth.example.org -> login.auth-example-org.192.168.1.91.sslip.io- В
Location,Set-Cookie,Access-Control-Allow-Originи других текстовых заголовках домен будет переписан обратно на локальный. - В текстовых телах ответа также будут заменены абсолютные URL, хосты с портом и сами hostname.
По умолчанию это не локальный DNS-сервер. Прокси просто использует публичный wildcard DNS, например sslip.io, где:
192.168.1.91.sslip.io -> 192.168.1.91api.192.168.1.91.sslip.io -> 192.168.1.91
Поэтому браузер может открыть и основной локальный домен, и его сабдомены без отдельного DNS-сервера и без /etc/hosts.
При HTTPS_ENABLED=1 прокси:
- генерирует локальный CA;
- генерирует сертификат для каждого входящего
Host; - использует
SNI, чтобы наapi.<local-domain>иwww.<local-domain>отдавать отдельные сертификаты; - если старые локальные сертификаты были выпущены без нужных TLS-расширений браузера, прокси автоматически перевыпустит их при следующем запуске.
Файлы хранятся в каталоге .local-tls.
Чтобы браузер перестал ругаться на сертификаты, импортируй CA из:
/path/to/local-domain-proxy/.local-tls/local-proxy-ca.cert.pem
Пример для macOS:
sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain /path/to/local-domain-proxy/.local-tls/local-proxy-ca.cert.pemПример для Windows (cmd от имени администратора):
certutil -addstore -f Root "C:\path\to\local-proxy-ca.cert.pem"Пример для Windows PowerShell от имени администратора:
Import-Certificate -FilePath "C:\path\to\local-proxy-ca.cert.pem" -CertStoreLocation Cert:\LocalMachine\RootЕсли не хочется использовать sslip.io, можно как и раньше прописать имена в /etc/hosts, например:
127.0.0.1 local.test api.local.test static.local.test
Тогда запускай с LOCAL_DOMAIN=local.test.
- Переписывание тела делается только для текстовых content-type.
- Бинарные ответы (
images,fonts,archives) проксируются без изменений. - Для WebSocket прокси переписывает
Host,Origin,Refererи upgrade-заголовки, но не изменяет сами фреймы внутри соединения.