Question

I have node-reverse-proxy set up like this:

var options = {
  pathnameOnly: true,
  router: {
    '/myapp': '127.0.0.1:9000',
  }
}
httpProxy.createServer(options).listen(8000);

The webapp at the root of 9000 has an index.html file with a stylesheet link like this:

<link rel="stylesheet" href="styles/blue.css">

When I hit localhost:9000 directly the html is loaded and the css is found. Then I hit it through the reverse proxy at localhost:8000/myapp, however I get an Error 404 because localhost:9000/styles/blue.css is not found, because the file is served apparently at localhost:9000/myapp/styles/blue.css.

The html of my app of course doesn't know about the reverse proxy so I can't fix this in the index.html. So I guess I'm missing something basic about the setup of the proxy??

Était-ce utile?

La solution

I don't know if you've found a workaround for this by now, but I had the exact same problem and think I've worked it out.

When you hit the target endpoint directly localhost:9000 the path is sent through as /, ie just the root. Then when it requests the css etc, it resolves those as expected, from the root.

When you call via the proxy localhost:8000/myapp it routes /myapp to the target and then passes the target service / as the path, at which point it behaves exactly as above.

The problem here is actually at the browser. Because the path is specified as /myapp, it assumes that "myapp" is a filename. Then when it requests the css etc, it peels that back to the last slash and uses that as the base for all relative hrefs, which is why it then asks for /styles/blue.css and not /myapp/styles/blue.css. If you try browsing to localhost:8000/myapp/ - note the trailing slash - then it works because the browser now treats /myapp/ as a component of the path.

Obviously, you can't dictate that users must add the trailing slash. And you can't easily do much about it in the target service, if only because it doesn't see any difference, and anyway you don't want to have to repeat the same solution in every end service. So the answer seems to be to intercept it in the proxy.

This example works, though there might be a more robust way of implementing it. It looks out for cases where the last part of the path is probably a directory component, in which case the request is redirected to force the browser to re-issue it with a trailing slash. It makes that decision based on it not looking like a filename.ext or already having a trailing slash:

httpProxy.createServer(function(req, res, proxy) {
    var parsed = url.parse(req.url);
    if(!parsed.pathname.match(/(\w+\.\w+|\/)$/)) {
        parsed.protocol = 'http';
        parsed.host = req.headers.host;
        parsed.pathname += '/';
        res.writeHead(301, {Location: url.format(parsed)});
        res.end();
    } else {
        proxy.proxyRequest(req, res);
    }
}, options);
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top