Reverse proxy into docker container sub-path

I’m using caddy v2. I want to reverse proxy two different different subdomains to the same docker container, with different sub-paths. For example if my docker container is called my_app, i want to proxy dashboard.example.com to my_app:80/dashboard and api.example.com to my_app:80/api.

I’ve tried following caddy configuration:

dashboard.example.com {
    encode gzip
    reverse_proxy my_app:80/dashboard
}
api.example.com {
    encode gzip
    reverse_proxy my_app:80/api
}

but it does not seem to work. Can you please tell me what I’m doing wrong here?

In Caddy v2, you can’t specify subpaths on reverse_proxy. Instead, you need to perform a rewrite before proxying:

api.example.com {
	encode gzip
	rewrite * /api{uri}
	reverse_proxy my_app:80
}

In Caddy v1, that rewrite was done for you implicitly.

1 Like

Thanks francis, the solution seems to work for simple HTML sites but since I’m using a nuxt app it is not working apparently because it is not loading the assets (.js and .css) correctly. For example if I try to access http://dashboard.example.com/_nuxt/app.js it throws a 404 error.

What path is your nuxt backend expecting then? Are you sure you need the /dashboard path prefixed? You might not need a rewrite for that one.

I’ve create an additional subdomain definition without rewrite rule, like so:

app.example.com { <----THIS IS NEW DEFINITION (IT WORKS)
    encode gzip
    reverse_proxy my_app:80
}
dashboard.example.com {
    encode gzip
    rewrite * /dashboard{uri}
    reverse_proxy my_app:80
}
api.example.com {
    encode gzip
    rewrite * /api{uri}
    reverse_proxy my_app:80
}

The site works for app.example.com definition (without rewrite rule) and all the assets are loading correctly. (see image below)

DeepinScreenshot_select-area_20200728230720

However, I need the subdomains to point to a sub-path (not root path like in app.example.com), and when i inspect the dashboard.example.com it shows no assets:

DeepinScreenshot_select-area_20200728230731

Are you sure your nuxt backend is properly configured to expect those paths?

What does your index.html look like (right-click > view source, or Elements tab)? Network tab? Those are more useful than the Sources tab for this.

Looks like you should be setting a base in your nuxt config:

1 Like

Yeah, I’m pretty sure that those paths work in backend. For example, when I visit app.example.com/dashboard/ (the subdomain with no rewrite rule) I can view the dashboard page just fine.

However, if I visit the same page via dashboard.example.com the view is completely blank. Although, in this case when I view the page source, the page has its HTML loaded and it looks like this:

However, if I change the router base property as you suggested (for example with '/dashboard` value), it simply appends the that base to JS resource uri, like so:

“Network” tab shows following erros when dashboard.example.com is visited:

I’m inclined to think this issue lies with the upstream server.

I’ve tried to demonstrate Caddy’s behaviour here with a simple Caddyfile:

~/Projects/test
➜ caddy version
v2.1.1 h1:X9k1+ehZPYYrSqBvf/ocUgdLSRIuiNiMo7CvyGUQKeA=

~/Projects/test
➜ cat Caddyfile
http://:8081 {
  log
  respond "{uri}" 200
}

http://dashboard.example.com:8080 {
  rewrite * /dashboard{uri}
  reverse_proxy localhost:8081
}

http://api.example.com:8080 {
  rewrite * /api{uri}
  reverse_proxy localhost:8081
}

So we’re only getting 200 responses with a body containing the requested URI. Otherwise we’re replicating identical behaviour to your other sites. A good request is one where the URI is prefixed with the subdomain of the requested site. Giving it a quick shot:

~/Projects/test
➜ curl http://api.example.com:8080/stuff --resolve api.example.com:8080:127.0.0.1
/api/stuff⏎

2020/07/29 16:39:14.644	INFO	http.log.access	handled request	{"request": {"method": "GET", "uri": "/api/stuff", "proto": "HTTP/1.1", "remote_addr": "[::1]:53165", "host": "api.example.com:8080", "headers": {"User-Agent": ["curl/7.64.1"], "Accept": ["*/*"], "X-Forwarded-For": ["127.0.0.1"], "X-Forwarded-Proto": ["http"], "Accept-Encoding": ["gzip"]}}, "common_log": "::1 - - [30/Jul/2020:02:39:14 +1000] \"GET /api/stuff HTTP/1.1\" 200 10", "duration": 0.000052311, "size": 10, "status": 200, "resp_headers": {"Server": ["Caddy"], "Content-Type": []}}
~/Projects/test
➜ curl http://dashboard.example.com:8080/stuff --resolve dashboard.example.com:8080:127.0.0.1
/dashboard/stuff⏎

2020/07/29 16:39:18.317	INFO	http.log.access	handled request	{"request": {"method": "GET", "uri": "/dashboard/stuff", "proto": "HTTP/1.1", "remote_addr": "[::1]:53167", "host": "dashboard.example.com:8080", "headers": {"X-Forwarded-For": ["127.0.0.1"], "X-Forwarded-Proto": ["http"], "Accept-Encoding": ["gzip"], "User-Agent": ["curl/7.64.1"], "Accept": ["*/*"]}}, "common_log": "::1 - - [30/Jul/2020:02:39:18 +1000] \"GET /dashboard/stuff HTTP/1.1\" 200 16", "duration": 0.000037235, "size": 16, "status": 200, "resp_headers": {"Content-Type": [], "Server": ["Caddy"]}}

Everything is checking out, in terms of server behaviour. How the upstream is discriminating between the two is unclear to me. A client requesting /dashboard/foo to a site with no rewriting should result in an identical path to a client requesting /foo to a site that appends /dashboard, as shown above.

3 Likes

I think you are right. The issue seems with how a nuxt app behaves behind proxy when a sub-path is called. I tried to replicate the same behavior with an express server and it is working just fine.

I’ve opened an issue on nuxt github repo, let see what they have to say about it: https://github.com/nuxt/nuxt.js/issues/7804

Thanks for helping out.

2 Likes

Good Help. This solved my hours of troubleshooting

@francislavoie @Whitestrake

Well I got an update on the said issue. Here is the reply from one of nuxt team members:

It is expected behavior since you are rewriting URLs to /private{uri} , You are changing URLs of static files too and it ends with 404.
For example app.js is located in _nuxt/static/[some-random]/app.js but after your rewrite Caddy will request private/_nuxt/static/[some-random]/app.js from Nuxt. This file does not exists and it leads to 404

Remove /private prefix from URLs with _nuxt prefix and all static files(png, jpg, gif, …).

So I need to rewrite all urls to /private{uri} except for files located under /_nuxt/ folder. Now my Caddyfile looks like this:

dashboard.example.me {
    encode gzip
    rewrite * /dashboard/{uri}
    rewrite /_nuxt/ /{uri}
    reverse_proxy my_app:80
}

However, I’m still getting 404 error on all JS files. Is there any issue with my Caddyfile configuration?

If I’m reading this correctly, you only want to do rewrite * /dashboard/{uri} if the URL doesn’t start with /_nuxt, right? Then you’ll want to use a not + path matcher:

@notNuxt not path /_nuxt*
rewrite @notNuxt /dashboard/{uri}

But I’m still just grasping at straws here, I’m still not sure how this is all meant to look :grimacing:

1 Like

Yeah, I’m sure that’s what he meant. I’ve gone ahead and tried your provided snippet and it partially works. That means, JS files are now loading correctly but it is loading the root page located at my_app:80 and not my_app:80/dashboard/ (see image below).

I’m not sure why that is, so I’ll have to ask the nuxt team again.