I can't get socket.io proxy to work on v2

1. Caddy version (caddy version):

v2.0.0 h1:pQSaIJGFluFvu8KDGDODV8u4/QRED/OPyIR+MWYYse8=

2. How I run Caddy:

I am trying to reverse proxy a socket.io server from https://example.vaaski.dev/test to localhost:6969/test. I’m used Caddy v1 for over a year now and I’m impressed how good it runs after setting it up. But it seems like support for v1 is phasing out soon and I’d like to switch to v2.
My main usecase is socket.io servers, which I’d like to proxy from a path on my subdomain to a local port. My (working) v1 config looks like this:

server.vaaski.dev {
  proxy /path localhost:6969/socket.io {
    header_upstream Host {host}
    header_upstream X-Real-IP {remote}
    header_upstream X-Forwarded-For {remote}
    websocket
  }
  header /path {
    Access-Control-Allow-Origin https://app.vaaski.dev
    Access-Control-Allow-Credentials true
  }
}

And this is the config I am trying on v2 (not working):

server.vaaski.dev {
  respond /test "Hello World!"

  reverse_proxy /path {
    to localhost:6969/socket.io{uri}
    header_up Host {host}
    header_up X-Real-IP {remote}
    header_up X-Forwarded-For {remote}
  }
  header /path/* {
    Access-Control-Allow-Origin https://app.vaaski.dev
    Access-Control-Allow-Credentials true
  }
}

a. System environment:

Raspbian Lite GNU/Linux 10 (buster) on Raspberry Pi 3

b. Command:

caddy run --watch

d. My complete Caddyfile or JSON config:

see above

3. The problem I’m having:

I cannot get a socket.io connection to work on v2.

4. Error messages and/or full log output:

This depends on how exactly its configured.
With the v2 config above it just replies with an empty 200 OK to socket.io's GET request instead of some upgrade data, but with /* added to the matcher I run into a 500 error with caddy telling me something along the lines of ERROR http.log.error making dial info: upstream localhost/socket.io:6969{http.request.uri}: invalid dial address localhost/socket.io:6969/light/?EIO=3&transport=polling&t=NAwZ_lA

5. What I already tried:

I’ve tried many small adjustments and changes for the v2 config, such as adding /* to the end of the reverse_proxy matcher, adding and removing {uri} to the upstream, moving the port around in the upstream, but I can’t get it to work.

6. Links to relevant resources:

https://socket.io

I’d be happy to provide some example files to test for both v1 and v2 to test and any more info you could need. Thanks in advance <3

1 Like

You were on the right track!

In Caddy v2, path matching is exact-match, so you must specify * to allow any subpaths. /path will only allow /path and not /path/foo.

If you use /path*, it will work, but it will also allow /pathfoo. You could also do a redirect or rewrite from /path to /path/ and use /path/* if that matters for you.

Specifying {uri} there is incorrect, a proxy dial address is just the host/port to connect to. Caddy v2 doesn’t support paths in proxy addresses unlike v1, because it adds a bunch of complications. Instead, you can rewrite the URL before proxying to have the prefix you need.

Also, header_up X-Forwarded-For {remote} is not necessary, Caddy v2 does this for you already (and has slightly more correct logic to handle the case where you might have multiple proxies chained). The other two are likely not necessary either, I recommend you try removing them to see if it works without them. If your proxied service handles X-Forwarded-For, then X-Real-IP is not necessary, and the Host is already passed through transparently from the initial request, so it’s also likely unnecessary.

So with all that said, I think you’d want something like this (the rewrite/matcher is up to you to change if you care)

rewrite /path /path/
handle /path/* {
	uri strip_prefix /path
	rewrite * /socket.io{path}
	reverse_proxy localhost:6969 {
		header_up Host {host}
		header_up X-Real-IP {remote}
	}
}

FYI, in Caddy v2.1 (beta 1 is already out if you want to try it), you could use handle_path instead and remove the uri strip_prefix line – handle_path will be a shortcut to that construct.

I use handle here to isolate the rewrites from the rest of your config so that they don’t affect other services/directives, and it avoids needing to repeat the same /path/* matcher all over.

3 Likes

Absolutely amazing, it worked right away!
Thank you so much, I’ve been trying to get it to work for hours yesterday night, searching for a solution for so long that my girlfriend got mad haha.
Finally I can fully switch to v2 :blush:

I couldn’t find anything, but is there a way to tip you or the Caddy project a coffee or beer for your amazing work and light-speed fast help?

4 Likes

Consider sponsoring @matt, and therefore the project :smile:

2 Likes

This topic was automatically closed after 30 days. New replies are no longer allowed.