express-http-proxy vs http-proxy vs http-proxy-middleware
HTTP Proxy Libraries
express-http-proxyhttp-proxyhttp-proxy-middlewareSimilar Packages:

HTTP Proxy Libraries

HTTP Proxy Libraries in Node.js are tools that allow developers to create proxy servers or middleware that can forward HTTP requests to other servers. These libraries are useful for tasks such as load balancing, API gateway implementation, and handling cross-origin resource sharing (CORS) issues. They provide functionalities to intercept, modify, and forward requests and responses between clients and servers, enabling more control over the communication process. These libraries help streamline the development of proxy servers, improve performance, and enhance security by managing how requests are routed and handled.

Npm Package Weekly Downloads Trend

3 Years

Github Stars Ranking

Stat Detail

Package
Downloads
Stars
Size
Issues
Publish
License
express-http-proxy01,251150 kB1479 months agoMIT
http-proxy014,137-6196 years agoMIT
http-proxy-middleware011,13295.6 kB2718 minutes agoMIT

Feature Comparison: express-http-proxy vs http-proxy vs http-proxy-middleware

Integration with Express.js

  • express-http-proxy:

    express-http-proxy is designed specifically for Express.js applications, making it easy to integrate and use within your existing Express routes. It allows you to proxy requests by simply adding a middleware function to your Express app.

  • http-proxy:

    http-proxy is a standalone library that can be used with any Node.js HTTP server, including Express. However, it does not provide built-in integration with Express, so you need to handle the integration manually.

  • http-proxy-middleware:

    http-proxy-middleware is built as middleware for Express.js and other Connect-based frameworks, providing seamless integration. It allows you to define proxy rules directly within your Express app, making it easy to manage multiple proxy endpoints.

Configuration and Customization

  • express-http-proxy:

    express-http-proxy offers a simple API for configuring the proxy target and allows you to customize the request and response handling through hooks. You can modify headers, handle errors, and transform data as needed, but it is designed to be straightforward and easy to use.

  • http-proxy:

    http-proxy provides extensive configuration options, including support for custom routing, load balancing, and WebSocket proxying. It allows you to create highly customizable proxy servers, but this flexibility comes with increased complexity.

  • http-proxy-middleware:

    http-proxy-middleware allows you to configure proxy rules using a simple API, including support for path rewriting, header modification, and error handling. It is designed to be easy to configure while providing enough flexibility for most use cases.

WebSocket Support

  • express-http-proxy:

    express-http-proxy does not natively support WebSocket proxying. It is primarily focused on HTTP requests and responses, making it less suitable for applications that require real-time communication over WebSockets.

  • http-proxy:

    http-proxy has built-in support for proxying WebSocket connections, making it a good choice for applications that need to handle both HTTP and WebSocket traffic. This feature allows you to create proxy servers that can manage real-time communication alongside traditional HTTP requests.

  • http-proxy-middleware:

    http-proxy-middleware supports WebSocket proxying as well, but it requires explicit configuration. It is a good option for applications that need to proxy WebSocket connections within an Express.js middleware setup.

Ease of Use: Code Examples

  • express-http-proxy:

    Simple proxy with express-http-proxy

    const express = require('express');
    const proxy = require('express-http-proxy');
    const app = express();
    
    app.use('/api', proxy('https://api.example.com'));
    
    app.listen(3000, () => {
      console.log('Server is running on port 3000');
    });
    
  • http-proxy:

    Basic proxy server with http-proxy

    const http = require('http');
    const httpProxy = require('http-proxy');
    
    const proxy = httpProxy.createProxyServer({ target: 'https://api.example.com' });
    
    const server = http.createServer((req, res) => {
      proxy.web(req, res);
    });
    
    server.listen(3000, () => {
      console.log('Proxy server is running on port 3000');
    });
    
  • http-proxy-middleware:

    Proxy middleware with http-proxy-middleware

    const express = require('express');
    const { createProxyMiddleware } = require('http-proxy-middleware');
    const app = express();
    
    app.use('/api', createProxyMiddleware({ target: 'https://api.example.com', changeOrigin: true }));
    
    app.listen(3000, () => {
      console.log('Server is running on port 3000');
    });
    

How to Choose: express-http-proxy vs http-proxy vs http-proxy-middleware

  • express-http-proxy:

    Choose express-http-proxy if you are working within an Express.js application and need a simple way to proxy requests to another server. It is ideal for scenarios where you want to forward requests while having the ability to modify them or the responses easily.

  • http-proxy:

    Choose http-proxy if you need a low-level, highly configurable proxy server that can handle both HTTP and WebSocket connections. It is suitable for applications that require fine-grained control over the proxying process, including custom routing, load balancing, and handling complex scenarios.

  • http-proxy-middleware:

    Choose http-proxy-middleware if you need a middleware solution for Express.js or other Connect-based frameworks that allows you to create proxy endpoints with minimal configuration. It is particularly useful for setting up proxies in development environments, such as forwarding API requests to a backend server while working on a frontend application.

README for express-http-proxy

express-http-proxy NPM version Build Status

Express middleware to proxy request to another host and pass response back to original caller.

Install

$ npm install express-http-proxy --save

Usage

proxy(host, options);

Example:

To proxy URLS starting with '/proxy' to the host 'www.google.com':

var proxy = require('express-http-proxy');
var app = require('express')();

app.use('/proxy', proxy('www.google.com'));

30k view

The proxy middleware:

  • proxies request to your server to an arbitrary server, and
  • provide hooks to decorate and filter requests to the proxy target, and
  • provide hooks you to decorate and filter proxy responses before returning them to the client.

Client                    Express App                Proxy Middleware                Target Server
  |                           |                            |                             |
  | HTTP Request              |                            |                             |
  |-------------------------->|                            |                             |
  |                           | Request                    |                             |
  |                           |--------------------------->|                             |
  |                           |                            | +------------------------+  |
  |                           |                            | | Request Preprocessing  |  |
  |                           |                            | | 1. filter requests     |  |
  |                           |                            | | 2. resolve proxy host  |  |
  |                           |                            | | 3. decorate proxy opts |  |
  |                           |                            | | 4. decorate proxy req  |  |
  |                           |                            | | 5. resolve req path    |  |
  |                           |                            | +------------------------+  |
  |                           |                            | Forwarded Request           |
  |                           |                            |---------------------------->|
  |                           |                            |                             |
  |                           |                            | Response with Headers       |
  |                           |                            |<----------------------------|
  |                           |                            |                             |
  |                           |                            | +------------------------+  |
  |                           |                            | | Response Processing    |  |
  |                           |                            | | 1. skip to next?       |  |
  |                           |                            | | 2. copy proxy headers  |  |
  |                           |                            | | 3. decorate headers    |  |
  |                           |                            | | 4. decorate response   |  |
  |                           |                            | +------------------------+  |
  |                           |                            |                             |
  |                           | Modified Response          |                             |
  |                           |<---------------------------|                             |
  | Final Response            |                            |                             |
  |<--------------------------|                            |                             |
  |                           |                            |                             |

Streaming

Proxy requests and user responses are piped/streamed/chunked by default.

If you define a response modifier (userResDecorator, userResHeaderDecorator), or need to inspect the response before continuing (maybeSkipToNext), streaming is disabled, and the request and response are buffered. This can cause performance issues with large payloads.

Promises

Many function hooks support Promises. If any Promise is rejected, next(x) is called in the hosting application, where x is whatever you pass to Promise.reject;

e.g.

  app.use(proxy('/reject-promise', {
    proxyReqOptDecorator: function() {
      return Promise.reject('An arbitrary rejection message.');
    }
  }));

eventually calls

next('An arbitrary rejection messasage');

Host

The first positional argument is for the proxy host; in many cases you will use a static string here, eg.

app.use('/', proxy('http://google.com'))

However, this argument can also be a function, and that function can be memoized or computed on each request, based on the setting of memoizeHost.

function selectProxyHost() {
  return (new Date() % 2) ? 'http://google.com' : 'http://altavista.com';
}

app.use('/', proxy(selectProxyHost));

Notie: Host is only the host name. Any params after in url will be ignored. For http://google.com/myPath`, myPathwill be ignored because the host name isgoogle.com. See proxyReqPathResolver`` for more detailed path information.

Middleware mixing

If you use 'https://www.npmjs.com/package/body-parser' you should declare it AFTER the proxy configuration, otherwise original 'POST' body could be modified and not proxied correctly.

app.use('/proxy', proxy('http://foo.bar.com'))

// Declare use of body-parser AFTER the use of proxy
app.use(bodyParser.foo(bar))
app.use('/api', ...)

If this cannot be avoided and you MUST proxy after body-parser has been registered, set parseReqBody to false and explicitly specify the body you wish to send in proxyReqBodyDecorator.

app.use(bodyParser.foo(bar))

app.use('/proxy', proxy('http://foo.bar.com', {
  parseReqBody: false,
  proxyReqBodyDecorator: function () {

  },
}))

Options

proxyReqPathResolver (supports Promises)

Note: In express-http-proxy, the path is considered the portion of the url after the host, and including all query params. E.g. for the URL http://smoogle.com/search/path?q=123; the path is /search/path?q=123. Authors using this resolver must also handle the query parameter portion of the path.

Provide a proxyReqPathResolver function if you'd like to operate on the path before issuing the proxy request. Use a Promise for async operations.

  app.use(proxy('localhost:12345', {
    proxyReqPathResolver: function (req) {
      var parts = req.url.split('?');
      var queryString = parts[1];
      var updatedPath = parts[0].replace(/test/, 'tent');
      return updatedPath + (queryString ? '?' + queryString : '');
    }
  }));

Promise form

app.use('/proxy', proxy('localhost:12345', {
  proxyReqPathResolver: function(req) {
    return new Promise(function (resolve, reject) {
      setTimeout(function () {   // simulate async
        var parts = req.url.split('?');
        var queryString = parts[1];
        var updatedPath = parts[0].replace(/test/, 'tent');
        var resolvedPathValue = updatedPath + (queryString ? '?' + queryString : '');
        resolve(resolvedPathValue);
      }, 200);
    });
  }
}));

forwardPath

DEPRECATED. See proxyReqPathResolver

forwardPathAsync

DEPRECATED. See proxyReqPathResolver

filter (supports Promises)

The filter option can be used to limit what requests are proxied. Return true to continue to execute proxy; return false-y to skip proxy for this request.

For example, if you only want to proxy get request:

app.use('/proxy', proxy('www.google.com', {
  filter: function(req, res) {
     return req.method == 'GET';
  }
}));

Promise form:

  app.use(proxy('localhost:12346', {
    filter: function (req, res) {
      return new Promise(function (resolve) {
        resolve(req.method === 'GET');
      });
    }
  }));

Note that in the previous example, resolve(false) will execute the happy path for filter here (skipping the rest of the proxy, and calling next()). reject() will also skip the rest of proxy and call next().

userResDecorator (was: intercept) (supports Promise)

You can modify the proxy's response before sending it to the client.

app.use('/proxy', proxy('www.google.com', {
  userResDecorator: function(proxyRes, proxyResData, userReq, userRes) {
    data = JSON.parse(proxyResData.toString('utf8'));
    data.newProperty = 'exciting data';
    return JSON.stringify(data);
  }
}));
app.use(proxy('httpbin.org', {
  userResDecorator: function(proxyRes, proxyResData) {
    return new Promise(function(resolve) {
      proxyResData.funkyMessage = 'oi io oo ii';
      setTimeout(function() {
        resolve(proxyResData);
      }, 200);
    });
  }
}));
304 - Not Modified

When your proxied service returns 304, not modified, this step will be skipped, since there is no body to decorate.

exploiting references

The intent is that this be used to modify the proxy response data only.

Note: The other arguments (proxyRes, userReq, userRes) are passed by reference, so you can currently exploit this to modify either response's headers, for instance, but this is not a reliable interface. I expect to close this exploit in a future release, while providing an additional hook for mutating the userRes before sending.

gzip responses

If your proxy response is gzipped, this program will automatically unzip it before passing to your function, then zip it back up before piping it to the user response. There is currently no way to short-circuit this behavior.

limit

This sets the body size limit (default: 1mb). If the body size is larger than the specified (or default) limit, a 413 Request Entity Too Large error will be returned. See bytes.js for a list of supported formats.

app.use('/proxy', proxy('www.google.com', {
  limit: '5mb'
}));

memoizeHost

Defaults to true.

When true, the host argument will be parsed on first request, and memoized for subsequent requests.

When false, host argument will be parsed on each request.

E.g.,


  function coinToss() { return Math.random() > .5 }
  function getHost() { return coinToss() ? 'http://yahoo.com' : 'http://google.com' }

  app.use(proxy(getHost, {
    memoizeHost: false
  }))

In this example, when memoizeHost:false, the coinToss occurs on each request, and each request could get either value.

Conversely, When memoizeHost:true, the coinToss would occur on the first request, and all additional requests would return the value resolved on the first request.

userResHeaderDecorator

When a userResHeaderDecorator is defined, the return of this method will replace (rather than be merged on to) the headers for userRes.

Note that by default, headers from the PROXY response CLOBBER all headers that may have previously been set on the userResponse. Authors have the option of constructing any combination of proxyRes and userRes headers in the userResHeaderDecorator. Check the tests for this method for examples.

app.use('/proxy', proxy('www.google.com', {
  userResHeaderDecorator(headers, userReq, userRes, proxyReq, proxyRes) {
    // recieves an Object of headers, returns an Object of headers.
    return headers;
  }
}));

decorateRequest

REMOVED: See proxyReqOptDecorator and proxyReqBodyDecorator.

skipToNextHandlerFilter(supports Promise form)

(experimental: this interface may change in upcoming versions)

Allows you to inspect the proxy response, and decide if you want to continue processing (via express-http-proxy) or call next() to return control to express.

app.use('/proxy', proxy('www.google.com', {
  skipToNextHandlerFilter: function(proxyRes) {
    return proxyRes.statusCode === 404;
  }
}));

proxyErrorHandler

By default, express-http-proxy will pass any errors except ECONNRESET to next, so that your application can handle or react to them, or just drop through to your default error handling. ECONNRESET errors are immediately returned to the user for historical reasons.

If you would like to modify this behavior, you can provide your own proxyErrorHandler.

// Example of skipping all error handling.

app.use(proxy('localhost:12346', {
  proxyErrorHandler: function(err, res, next) {
    next(err);
  }
}));


// Example of rolling your own

app.use(proxy('localhost:12346', {
  proxyErrorHandler: function(err, res, next) {
    switch (err && err.code) {
      case 'ECONNRESET':    { return res.status(405).send('504 became 405'); }
      case 'ECONNREFUSED':  { return res.status(200).send('gotcher back'); }
      default:              { next(err); }
    }
}}));

proxyReqOptDecorator (supports Promise form)

You can override most request options before issuing the proxyRequest. proxyReqOpt represents the options argument passed to the (http|https).request module.

NOTE: req.path cannot be changed via this method; use proxyReqPathResolver instead. (see https://github.com/villadora/express-http-proxy/issues/243)

app.use('/proxy', proxy('www.google.com', {
  proxyReqOptDecorator: function(proxyReqOpts, srcReq) {
    // you can update headers
    proxyReqOpts.headers['Content-Type'] = 'text/html';
    // you can change the method
    proxyReqOpts.method = 'GET';
    return proxyReqOpts;
  }
}));

You can use a Promise for async style.

app.use('/proxy', proxy('www.google.com', {
  proxyReqOptDecorator: function(proxyReqOpts, srcReq) {
    return new Promise(function(resolve, reject) {
      proxyReqOpts.headers['Content-Type'] = 'text/html';
      resolve(proxyReqOpts);
    })
  }
}));

proxyReqBodyDecorator (supports Promise form)

You can mutate the body content before sending the proxyRequest.

app.use('/proxy', proxy('www.google.com', {
  proxyReqBodyDecorator: function(bodyContent, srcReq) {
    return bodyContent.split('').reverse().join('');
  }
}));

You can use a Promise for async style.

app.use('/proxy', proxy('www.google.com', {
  proxyReqBodyDecorator: function(proxyReq, srcReq) {
    return new Promise(function(resolve, reject) {
      http.get('http://dev/null', function (err, res) {
        if (err) { reject(err); }
        resolve(res);
      });
    })
  }
}));

https

Normally, your proxy request will be made on the same protocol as the host parameter. If you'd like to force the proxy request to be https, use this option.

app.use('/proxy', proxy('www.google.com', {
  https: true
}));

preserveHostHdr

You can copy the host HTTP header to the proxied express server using the preserveHostHdr option.

app.use('/proxy', proxy('www.google.com', {
  preserveHostHdr: true
}));

parseReqBody

The parseReqBody option allows you to control parsing the request body. For example, disabling body parsing is useful for large uploads where it would be inefficient to hold the data in memory.

Note: this setting is required for binary uploads. A future version of this library may handle this for you.

This defaults to true in order to preserve legacy behavior.

When false, no action will be taken on the body and accordingly req.body will no longer be set.

Note that setting this to false overrides reqAsBuffer and reqBodyEncoding below.

app.use('/proxy', proxy('www.google.com', {
  parseReqBody: false
}));

reqAsBuffer

Note: this is an experimental feature. ymmv

The reqAsBuffer option allows you to ensure the req body is encoded as a Node Buffer when sending a proxied request. Any value for this is truthy.

This defaults to to false in order to preserve legacy behavior. Note that the value of reqBodyEnconding is used as the encoding when coercing strings (and stringified JSON) to Buffer.

Ignored if parseReqBody is set to false.

app.use('/proxy', proxy('www.google.com', {
  reqAsBuffer: true
}));

reqBodyEncoding

Encoding used to decode request body. Defaults to utf-8.

Use null to preserve as Buffer when proxied request body is a Buffer. (e.g image upload) Accept any values supported by raw-body.

The same encoding is used in the intercept method.

Ignored if parseReqBody is set to false.

app.use('/post', proxy('httpbin.org', {
  reqBodyEncoding: null
}));

timeout

By default, node does not express a timeout on connections. Use timeout option to impose a specific timeout. Timed-out requests will respond with 504 status code and a X-Timeout-Reason header.

app.use('/', proxy('httpbin.org', {
  timeout: 2000  // in milliseconds, two seconds
}));

Trace debugging

The node-debug module is used to provide a trace debugging capability.

DEBUG=express-http-proxy npm run YOUR_PROGRAM
DEBUG=express-http-proxy npm run YOUR_PROGRAM  | grep 'express-http-proxy'   # to filter down to just these messages

Will trace the execution of the express-http-proxy module in order to aide debugging.

Upgrade to 1.0, transition guide and breaking changes

decorateRequest has been REMOVED, and will generate an error when called. See proxyReqOptDecorator and proxyReqBodyDecorator.

Resolution: Most authors will simply need to change the method name for their decorateRequest method; if author was decorating reqOpts and reqBody in the same method, this will need to be split up.

intercept has been REMOVED, and will generate an error when called. See userResDecorator.

Resolution: Most authors will simply need to change the method name from intercept to userResDecorator, and exit the method by returning the value, rather than passing it to a callback. E.g.:

Before:

app.use('/proxy', proxy('www.google.com', {
  intercept: function(proxyRes, proxyResData, userReq, userRes, cb) {
    data = JSON.parse(proxyResData.toString('utf8'));
    data.newProperty = 'exciting data';
    cb(null,  JSON.stringify(data));
  }
}));

Now:

app.use('/proxy', proxy('www.google.com', {
  userResDecorator: function(proxyRes, proxyResData, userReq, userRes) {
    data = JSON.parse(proxyResData.toString('utf8'));
    data.newProperty = 'exciting data';
    return JSON.stringify(data);
  }
}));

forwardPath and forwardPathAsync have been DEPRECATED and will generate a warning when called. See proxyReqPathResolver.

Resolution: Simple update the name of either forwardPath or forwardPathAsync to proxyReqPathResolver.

When errors occur on your proxy server

When your proxy server responds with an error, express-http-proxy returns a response with the same status code. See test/catchingErrors for syntax details.

When your proxy server times out, express-http-proxy will continue to wait indefinitely for a response, unless you define a timeout as described above.

Questions

Q: Does it support https proxy?

The library will automatically use https if the provided path has 'https://' or ':443'. You may also set option https to true to always use https.

You can use proxyReqOptDecorator to ammend any auth or challenge headers required to succeed https.

Q: How can I support non-standard certificate chains?

You can use the ability to decorate the proxy request prior to sending. See proxyReqOptDecorator for more details.

app.use('/', proxy('internalhost.example.com', {
  proxyReqOptDecorator: function(proxyReqOpts, originalReq) {
    proxyReqOpts.ca =  [caCert, intermediaryCert]
    return proxyReqOpts;
  }
})

Q: How to ignore self-signed certificates ?

You can set the rejectUnauthorized value in proxy request options prior to sending. See proxyReqOptDecorator for more details.

app.use('/', proxy('internalhost.example.com', {
  proxyReqOptDecorator: function(proxyReqOpts, originalReq) {
    proxyReqOpts.rejectUnauthorized = false
    return proxyReqOpts;
  }
}))

Release Notes

ReleaseNotes
2.1.2Fixes => content-length request header is removed when parseReqBody is false (#549), implement memory leak fix (#566), fix to UserDecorator, replace httpbin in test suite, general cleanup of test and dot files
2.1.1(trivial) Fixes formatting in README.
2.1.0Fixes parsing error in content-types. Improves behavior of proxyReqBodyDecorator when parseReqBody=false. Repairs issue where authors can't use proxy() twice in Express middleware stack. Fix new Buffer deprecation warning.
2.0.0Update all dependencies; set stage for next iteration. express-http-proxy interface has not changed, but the underlying libraries are not guaranteed to be backward compatible. Versions beyond this point are expected to be run in node verions >= 16.
----------------------------------------------------------------------------
1.6.3[#453] Author should be able to delete headers in userResHeaderDecorator.
1.6.2Update node.js versions used by ci.
1.6.1Minor bug fixes and documentation.
1.6.0Do gzip and gunzip aysyncronously. Test and documentation improvements, dependency updates.
1.5.1Fixes bug in stringifying debug messages.
1.5.0Fixes bug in filter signature. Fix bug in skipToNextHandler, add expressHttpProxy value to user res when skipped. Add tests for host as ip address.
1.4.0DEPRECATED. Critical bug in the filter api.
1.3.0DEPRECATED. Critical bug in the filter api. filter now supports Promises. Update linter to eslint.
1.2.0Auto-stream when no decorations are made to req/res. Improved docs, fixes issues in maybeSkipToNexthandler, allow authors to manage error handling.
1.1.0Add step to allow response headers to be modified.
1.0.7Update dependencies. Improve docs on promise rejection. Fix promise rejection on body limit. Improve debug output.
1.0.6Fixes preserveHostHdr not working, skip userResDecorator on 304, add maybeSkipToNext, test improvements and cleanup.
1.0.5Minor documentation and test patches
1.0.4Minor documentation, test, and package fixes
1.0.3Fixes 'limit option is not taken into account
1.0.2Minor docs corrections.
1.0.1Minor docs adjustments.
1.0.0Major revision.
REMOVE decorateRequest, ADD proxyReqOptDecorator and proxyReqBodyDecorator.
REMOVE intercept, ADD userResDecorator
userResDecorator supports a Promise form for async operations.
General cleanup of structure and application of hooks. Documentation improvements. Update all dependencies. Re-organize code as a series of workflow steps, each (potentially) supporting a promise, and creating a reusable pattern for future development.
0.11.0Allow author to prevent host from being memoized between requests. General program cleanup.
0.10.1Fixed issue where 'body encoding' was being incorrectly set to the character encoding.
Dropped explicit support for node 0.10.
Intercept can now deal with gziped responses.
Author can now 'force https', even if the original request is over http.
Do not call next after ECONNRESET catch.
0.10.0Fix regression in forwardPath implementation.
0.9.1Documentation updates. Set 'Accept-Encoding' header to match bodyEncoding.
0.9.0Better handling for request body when body is JSON.
0.8.0Features: add forwardPathAsync option
Updates: modernize dependencies
Fixes: Exceptions parsing proxied response causes error: Can't set headers after they are sent. (#111)
If client request aborts, proxied request is aborted too (#107)
0.7.4Move jscs to devDependencies to avoid conflict with nsp.
0.7.3Adds a timeout option. Code organization and small bug fixes.
0.7.2Collecting many minor documentation and test improvements.
0.4.0Signature of intercept callback changed from function(data, req, res, callback) to function(rsp, data, req, res, callback) where rsp is the original response from the target

Licence

MIT