Skip to content

express.use(path, proxy()) ignores path for websockets #204

Open

Description

@ftes

Also see expressjs/express#3428.

Expected behavior

The following should listen for websocket requests only under /__webpack_hmr.

express.use('/__webpack_hmr', proxy({ ws: true, target: 'http://...' }));

Websocket requests to other paths (e.g. /socket.io) should not be handled by the proxy.

Actual behavior

The proxy "hijacks" all incoming websocket requests, also those to /socket.io (see socketio/socket.io#3062).

Setup

Fiddle: https://github.com/ftes/socket.io-fiddle/pull/1/files

  • http-proxy-middleware: 0.17.4
  • server: express 4.13.4
  • socket.io: 2.0.3

proxy middleware configuration

proxy({ ws: true, target: 'http://localhost:4000' });

server mounting

var app = express();

app.use('/__webpack_hmr', proxy);
app.listen(3000);

Workaround

Pass path /__webpack_hmr as context to proxy.

const webpackHmrProxy = proxy('/__webpack_hmr', {
  target: 'http://localhost:4000',
  ws: true,
});
app.use(webpackHmrProxy);

Activity

  1. chimurai commented on Sep 27, 2017

    @chimurai
    Owner

    Thanks for reporting this @ftes .
    I'll have to investigate this.

  2. roccomuso commented on Sep 27, 2017

    @roccomuso

    I'm having the same issue..
    I kinda fixed it passing the "context" twice:

    express.use('/__webpack_hmr', proxy('/__webpack_hmr', { ws: true, target: 'http://...' }))

    @ftes give it a try.

  3. mattbrunetti commented on Nov 6, 2017

    @mattbrunetti

    Slightly off topic:

    @roccomuso You don't need the first occurrence of "context" (passed to express.use) though do you?

    I would have thought it would work the same (with or without passing context to express.use), but for some reason, when I pass the context in both ways, and I restart the server, reconnection in the browser fails with WebSocket connection to 'ws://localhost:3000/api/socket.io/?EIO=3&transport=websocket' failed: Connection closed before receiving a handshake response even after I refresh the page. If I only pass the context to proxy then I get those errors only until I refresh the page. My head hurts. No idea what's going on.

  4. janneh commented on Nov 24, 2017

    @janneh

    I'm experiencing a similar issue.

    Where:

    app.use("/example", proxy({ target: "www.example.com" }))

    Gives:

    [HPM] Proxy created: /  ->  http://www.example.com

    And

    app.use(proxy("/example", { target: "www.example.com" }))

    Gives:

    [HPM] Proxy created: /example  ->  http://www.example.com
    "node": "8.9.0"
    "express": "^4.16.2",
    "http-proxy-middleware": "^0.17.4",

    Just wanted to put it here since the README example describes it as the first one...

  5. DonovanCharpin commented on Oct 12, 2018

    @DonovanCharpin

    Oh my god, thanks @roccomuso!

    I was using this code which was working fine when the socket had a successful connection but was throwing an error when the WS_URI was returning an error (404, 500 ...)

    server.use(proxy("/ws", {
        target: process.env.WS_URI,
        ws: true
      })
    );
    

    I just updated my code with this code and it worked. The second route in the proxy method doesn't seem to be required though. I didn't see my route was not handled by express...

    server.use("/ws", proxy("/ws", {
        target: process.env.WS_URI,
        ws: true
      })
    );
    
  6. kdemarest commented on May 10, 2019

    @kdemarest

    I have the same issue., BUT http-proxy-middleware WORKS when I proxy traffic from "/".

    In the broken case, HTTP traffic routes through the proxy, but the proxy fails to upgrade to websockets.

    Here is my use() statement. it appears before all other use() statements.:

    let wsProxy = Proxy( '/shadowStone', {
    	ws: true,
    	target: 'ws://127.0.0.1:3000',
    	pathRewrite: {
    		'^/shadowStone': '',
    	},
    	logLevel: 'debug',
    });
    app.use( '/shadowStone', wsProxy );
    

    And, just in case, right after the server.listen() I have put:

    server.on('upgrade', wsProxy.upgrade)
    

    The error I am seeing is:

    404 error attempting to reach
    ?EIO=3&transport=polling&t=MgVfhrd
    /socket.io
    

    And a later use() statement reports that it is seeing the following:

    GET /socket.io/?EIO=3&transport=polling&t=MgVfhrd
    

    When I change my code to read as follows, it WORKS just fine:

    	let wsProxy = Proxy( '/', {
    		ws: true,
    		target: 'ws://127.0.0.1:3000',
    //		pathRewrite: {
    //			'^/shadowStone': '',
    //		},
    		logLevel: 'debug',
    	})
    
    	app.use( '/', wsProxy );
    
  7. kdemarest commented on May 10, 2019

    @kdemarest

    I found my issue. I was using socket.io, which is very particular about how you initialize it. In the target of the proxy, in index.html, I was starting socket.io with:

    this.socket = io.connect('http://localhost:3000/shadowStone');
    

    when I should have been starting it with:

    this.socket = io.connect('http://localhost:3000', {'path': '/shadowStone/socket.io'});
    

    and the server-side companion code to this is

    app.use( '/shadowStone', Express.static('.') )
    let server = app.listen(3000)
    io = SocketIo(server, {path:'/shadowStone/socket.io'});
    

    In my proxy, the code is quite simple:

    let wsProxy = Proxy({
    	target: 'ws://localhost:3000',
    	logLevel: 'debug',
    });
    
    app.use( '/shadowStone', wsProxy );
    let server = app.listen(port);
    server.on('upgrade', wsProxy.upgrade)
    

    Hope this helps somebody with a similar problem...

  8. rafalp commented on Apr 26, 2020

    @rafalp

    Is there any workaround for this in current version? I'm trying to proxy WS to my server in create-react-app without breaking reloader that listens on different path.

  9. rafalp commented on May 26, 2020

    @rafalp

    I've changed this:

    
    module.exports = function(app) {
      app.use(
        '/sockjs-node',
        createProxyMiddleware(
          {
            target: 'ws://localhost:3000',
            ws: true,
          }
        )
      );
      app.use(
        '/graphql',
        createProxyMiddleware(
          {
            target: 'http://localhost:8000',
            changeOrigin: true,
            ws: true,
          }
        )
      );
    };
    

    to this:

    
    module.exports = function(app) {
      app.use(
        '/sockjs-node',
        createProxyMiddleware(
          '/sockjs-node',
          {
            target: 'ws://localhost:3000',
            ws: true,
          }
        )
      );
      app.use(
        '/graphql',
        createProxyMiddleware(
          '/graphql',
          {
            target: 'http://localhost:8000',
            changeOrigin: true,
            ws: true,
          }
        )
      );
    };
    

    ...and its working now :)

  10. added a commit that references this issue on Mar 16, 2025
  11. John-Wickedsick commented on May 14, 2025

    @John-Wickedsick

    When I try this:

    I've changed this:

    
    module.exports = function(app) {
      app.use(
        '/sockjs-node',
        createProxyMiddleware(
          {
            target: 'ws://localhost:3000',
            ws: true,
          }
        )
      );
      app.use(
        '/graphql',
        createProxyMiddleware(
          {
            target: 'http://localhost:8000',
            changeOrigin: true,
            ws: true,
          }
        )
      );
    };
    

    to this:

    
    module.exports = function(app) {
      app.use(
        '/sockjs-node',
        createProxyMiddleware(
          '/sockjs-node',
          {
            target: 'ws://localhost:3000',
            ws: true,
          }
        )
      );
      app.use(
        '/graphql',
        createProxyMiddleware(
          '/graphql',
          {
            target: 'http://localhost:8000',
            changeOrigin: true,
            ws: true,
          }
        )
      );
    };
    

    ...and its working now :)

    I get an error that createProxyMiddleware only expects one argument

    So this doesnt work for me

    import { createProxyMiddleware } from 'http-proxy-middleware';
    import 'dotenv/config';
    
    export default function expressMiddleware(router) {
      router.use(
        '/api',
        createProxyMiddleware('/api',{
          target: process.env.BACKEND_URL + '/api',
          changeOrigin: true,
          logger: console,
          ws: true,
        }),
      );
    }

    How can I fix this with the new version?

  12. John-Wickedsick commented on May 14, 2025

    @John-Wickedsick

    Here is my solution:

    import { createProxyMiddleware } from 'http-proxy-middleware';
    import 'dotenv/config';
    
    export default function expressMiddleware(router) {
      router.use(
        createProxyMiddleware({
          pathFilter: '/api',
          target: process.env.BACKEND_URL,
          changeOrigin: true,
          logger: console,
          ws: true,
        }),
      );
    }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions