simple HTTP | HTTPS | WS | WSS reverse proxy in node.js. currently supports:
- reverse proxy using incoming request's
x-forwarded-hostorhostheader to pre-configured origin servers. see Host and X-Forwarded-Host - proxy incoming
http:orhttps:request to originhttp:orhttps:servers - proxy incoming
ws:orwss:request to originws:orwss:servers - supports graceful shutdowns
- supports round-robin origin server selection
- supports force upgrade
http:tohttps:andws:towss: - supports forced redirects for host migration
- supports URL parts preservation during redirects
- for global installation
npm i fimiproxy -g - for local installation
npm i fimiproxy - for local dev-dependency installation
npm i fimiproxy -D
replace npm with yarn or any other package manager of choice.
{
"exposeHttpProxy": false,
"httpPort": "",
"exposeHttpsProxy": false,
"httpsPort": "",
"exposeWsProxyForHttp": false,
"exposeWsProxyForHttps": false,
"httpsPublicKeyFilepath": "",
"httpsPrivateKeyFilepath": "",
"httpsPublicKey": "",
"httpsPrivateKey": "",
"debug": false,
"routes": [
{
"origin": [
{
"originHost": "",
"originPort": "",
"originProtocol": "http:"
},
{
"originHost": "",
"originPort": "",
"originProtocol": "ws:"
}
],
"incomingHostAndPort": "",
"forceUpgradeHttpToHttps": false,
"forceUpgradeWsToWss": false,
"forceRedirect": false,
"usePermanentRedirect": false,
"redirectHost": "",
"redirectURLParts": false,
"overrideHost": ""
}
],
"forceUpgradeHttpToHttps": false,
"forceUpgradeWsToWss": false,
"usePermanentRedirect": false,
"redirectHost": "",
"redirectURLParts": false
}exposeHttpProxy— set totrueto expose an HTTP server, requireshttpPortto be set iftrueexposeHttpsProxy— set totrueto expose an HTTPS server, requireshttpsPort,httpsPublicKeyORhttpsPublicKeyFilepath,httpsPrivateKeyORhttpsPrivateKeyFilepathto be set iftrueexposeWsProxyForHttp— set totrueto expose a WebSocket server for HTTP requests, requireshttpPortandexposeHttpProxyto be set iftrueexposeWsProxyForHttps— set totrueto expose a WebSocket server for HTTPS requests, requireshttpsPortandexposeHttpsProxyto be set iftruehttpPort— port HTTP server should listen on, whenexposeHttpProxyistruehttpsPort— port HTTPS server should listen on, whenexposeHttpsProxyistruehttpsPublicKeyFilepath— filepath to TLS certificate (public key) used with HTTPS serverhttpsPrivateKeyFilepath— filepath to TLS private key used with HTTPS serverhttpsPublicKey— TLS certificate (public key) string used with HTTPS server. takes precedence overhttpsPublicKeyFilepathhttpsPrivateKey— TLS private key string used with HTTPS server. takes precedence overhttpsPrivateKeyFilepathdebug— set totrueto enable debug logging for troubleshootingforceUpgradeHttpToHttps— set totrueto force upgrade allhttp:requests tohttps:requests globallyforceUpgradeWsToWss— set totrueto force upgrade allws:requests towss:requests globallyusePermanentRedirect— set totrueto use permanent redirect globally. The proxy server will return a308redirect response to the client instead of the default307temporary redirect responseredirectHost— default host to redirect to globally, e.g. when upgrading to HTTPS or WSS, or if the incoming host is no longer supported and all requests to it should be redirected somewhere else. if not set, the proxy server will redirect to the incomingx-forwarded-hostorhostheader fieldredirectURLParts— controls which URL parts are preserved during redirects. Can betrue(preserve all parts),false(preserve only host), or an object specifying which parts to preserve (see Route-level Configuration below)
routes— array of incoming host to origin protocol, host, and port mappingsorigin— array of origin server host, port, and protocol (supports round-robin load balancing)originHost— origin host or IP addressoriginPort— origin port numberoriginProtocol— origin protocol. one ofhttp:,https:,ws:, orwss:. don't forget the:at the end
incomingHostAndPort— incominghost:portpattern to match for proxying to origin server. picked from HTTPhostheader field. Examples:example.com:80,api.example.com,*.example.com(wildcards supported)forceUpgradeHttpToHttps— set totrueto force upgradehttp:requests tohttps:requests for this routeforceUpgradeWsToWss— set totrueto force upgradews:requests towss:requests for this routeforceRedirect— set totrueto force redirect all requests to this route to theredirectHost. useful for permanent host migrationsusePermanentRedirect— set totrueto use permanent redirect for this route. The proxy server will return a308redirect response to the client instead of the default307temporary redirect responseredirectHost— host to redirect to for this route, e.g. when upgrading to HTTPS or WSS, or whenforceRedirectis enabled. if not set, the proxy server will redirect to the incomingx-forwarded-hostorhostheader fieldredirectURLParts— controls which URL parts are preserved during redirects for this route. Can be:true— preserve all URL parts (protocol, pathname, search, username, password)false— preserve only the host- An object with specific parts:
{ "protocol": true, "pathname": true, "search": false, "username": false, "password": false }
overrideHost— if set, the proxy will override thehostandx-forwarded-hostheader fields in requests sent to the origin server. useful for testing or when a specific host is required (e.g., for OAuth callbacks)
{
"exposeHttpProxy": true,
"httpPort": "80",
"exposeHttpsProxy": true,
"httpsPort": "443",
"httpsPublicKeyFilepath": "/path/to/cert.pem",
"httpsPrivateKeyFilepath": "/path/to/key.pem",
"routes": [
{
"origin": [
{
"originHost": "localhost",
"originPort": 3000,
"originProtocol": "http:"
}
],
"incomingHostAndPort": "example.com",
"forceUpgradeHttpToHttps": true
}
]
}{
"exposeHttpsProxy": true,
"httpsPort": "443",
"httpsPublicKey": "-----BEGIN CERTIFICATE-----\n...",
"httpsPrivateKey": "-----BEGIN PRIVATE KEY-----\n...",
"routes": [
{
"origin": [
{
"originHost": "backend1.internal",
"originPort": 8080,
"originProtocol": "http:"
},
{
"originHost": "backend2.internal",
"originPort": 8080,
"originProtocol": "http:"
}
],
"incomingHostAndPort": "api.example.com"
}
]
}{
"exposeHttpProxy": true,
"httpPort": "80",
"routes": [
{
"origin": [],
"incomingHostAndPort": "old-domain.com",
"forceRedirect": true,
"redirectHost": "new-domain.com",
"usePermanentRedirect": true,
"redirectURLParts": {
"pathname": true,
"search": true
}
}
]
}{
"exposeHttpsProxy": true,
"httpsPort": "443",
"exposeWsProxyForHttps": true,
"httpsPublicKeyFilepath": "/path/to/cert.pem",
"httpsPrivateKeyFilepath": "/path/to/key.pem",
"routes": [
{
"origin": [
{
"originHost": "websocket-server.internal",
"originPort": 8080,
"originProtocol": "ws:"
}
],
"incomingHostAndPort": "ws.example.com",
"forceUpgradeWsToWss": true
}
]
}- if installed globally, run
fimiproxy ./path/to/config.json - if installed locally, run
npm exec fimiproxy ./path/to/config.json - for one-time run, run
npx -y fimiproxy ./path/to/config.json
Alternatively, you can start fimiproxy without passing a config filepath argument by setting the FIMIPROXY_CONFIG_FILEPATH environment variable:
# Set the environment variable
export FIMIPROXY_CONFIG_FILEPATH=./path/to/config.json
# Then run fimiproxy without arguments
fimiproxyOr in a single command:
FIMIPROXY_CONFIG_FILEPATH=./path/to/config.json fimiproxyNote: The command line argument takes precedence over the environment variable. If both are provided, the command line argument will be used.
import fimiproxy from "fimiproxy"
// start fimiproxy
await fimiproxy.startFimiproxyUsingConfig({
/** config */ {
exposeHttpProxy: true,
exposeHttpsProxy: true,
httpPort: "80",
httpsPort: "443",
debug: false,
routes: [{
origin: [{
originHost: "localhost",
originPort: 3000,
originProtocol: "https:",
}],
incomingHostAndPort: "www.example.com",
forceUpgradeHttpToHttps: true,
overrideHost: "localhost:3000"
}],
httpsPublicKey: "-----BEGIN CERTIFICATE-----\n...",
httpsPrivateKey: "-----BEGIN PRIVATE KEY-----\n...",
},
/** shouldHandleGracefulShutdown */ true,
/** exitProcessOnShutdown */ true,
});
// end fimiproxy
await fimiproxy.endFimiproxy(/** exitProcessOnShutdown */ true);startFimiproxyUsingConfig— start fimiproxy using configconfig: FimiproxyRuntimeConfig— see configuration aboveshouldHandleGracefulShutdown— defaults totrue. iftrue, will listen forSIGINTandSIGTERM, and attempt to gracefully shut down the proxy serverexitProcessOnShutdown— defaults totrue. ifshouldHandleGracefulShutdownistrue, will callprocess.exit()after graceful shutdown. your process may not shut down afterSIGINTandSIGTERMif nottrue. currently untested behaviour (if process will shutdown or not) when set tofalseandshouldHandleGracefulShutdownistrue
startFimiproxyUsingConfigFile— start fimiproxy using config read from filepathfilepath: string— file at filepath should be a json file, see configuration section above
startFimiproxyUsingProcessArgs— start fimiproxy using filepath picked fromprocess.argv[2]see https://nodejs.org/docs/latest/api/process.html#processargv. example,node your-script.js ./path/to/config.jsonstartFimiproxyUsingEnvVar— start fimiproxy using filepath from environment variable (defaults toFIMIPROXY_CONFIG_FILEPATH)endFimiproxy— gracefully end fimiproxyexitProcess— defaults totrue. callsprocess.exit()iftrue
Use fimiproxy to proxy local development servers with SSL termination:
fimiproxy dev-config.jsonRoute different subdomains to different microservices:
api.example.com→ backend API servicews.example.com→ WebSocket servicecdn.example.com→ static file server
Gradually migrate from old domain to new domain while preserving SEO:
- Use
forceRedirectwithusePermanentRedirect: true - Preserve URL paths and query parameters with
redirectURLParts
Distribute traffic across multiple backend servers using round-robin selection.
Set debug: true in your configuration or use the FIMIPROXY_DEBUG=true environment variable to see detailed logs.
-
EADDRINUSE Error: Port already in use
- Check if another process is using the port:
lsof -i :PORT - Use different ports in your configuration
- Check if another process is using the port:
-
SSL Certificate Issues:
- Ensure certificate files exist and are readable
- Verify certificate format (PEM)
- Check certificate expiration
-
WebSocket Connection Issues:
- Ensure
exposeWsProxyForHttporexposeWsProxyForHttpsis enabled - Verify origin server supports WebSocket protocol
- Check for protocol mismatch (ws vs wss)
- Ensure
-
Host Header Issues:
- Use
overrideHostif the origin server expects specific host headers - Check that
incomingHostAndPortmatches the actual request host
- Use
- Cannot sustain multiple start calls, because current state is managed using a module-global variable. We'll eventually transition to a class-based encapsulation system, so stick around (if you're versed in Typescript, you can contribute to this effort). Multiple start calls will either lead to existing servers being garbage collected or memory leak, I haven't tested it. So, call
endFimiproxybefore making another start call. Start calls are calls tostartFimiproxyUsingConfig,startFimiproxyUsingConfigFile, orstartFimiproxyUsingProcessArgs - Round-robin load balancing is simple rotation, not weighted or health-checked