How to serve file_server from different path?

:80 {
	@http {
	    path /ui/account
	}
	file_server @http {
	    root ../accounts/ui/build
	}
	reverse_proxy /api/account/* localhost:6677
}

I am trying to serve my api via localhost/api/account
and my ui at localhost/ui/account

How would i do this?

I think you’re looking for something like this:

:80 {
	handle_path /ui/account* {
		root * ../accounts/ui/build
		file_server
	}

	handle /api/account* {
		reverse_proxy  localhost:6677
	}

	handle {
		respond "Not found" 404
	}
}

When Caddy handles requests, it doesn’t automatically strip the path prefix when you use a matcher. The file_server handler assembles the path on disk by joining the root with the request path. So with your Caddyfile, the path Caddy was looking for on disk was ../accounts/ui/build/ui/account. To deal with this, we can use handle_path, which makes a subroute (i.e. handle) but also strips the path prefix on the given matcher.

Also, your config was leaving some paths unhandled. The default behaviour of Caddy is to return an empty 200 response if no handler was configured to handle that request. To resolve this, we use a handle that responds with a 404 as a fallback for the requests.

We also wrap the proxy in handle, because in the Caddyfile, directives are sorted by the directive order defined at the link below. You’ll notice that respond comes before reverse_proxy, so if we didn’t wrap them both in handle, the respond would always happen before reverse_proxy, making Caddy always serve 404s when you’d expect it to proxy.

If the Caddyfile adapter encounters the same directive more than once, it will sort them by their path matchers, longer first. This makes sure that the proxy handle will be ordered first, here.

Note that I’m assuming you’re running Caddy v2.2.0, there was a fix included in that version which ensures that handle_path and handle are sorted as though they are the same directive. This is important here so that the handle for the 404 doesn’t get sorted ahead of the file_server one.

This is just one way to solve this in the Caddyfile. There are other approaches that could be taken, for example using route to override directive order, or using not matchers to exclude relevant paths, but I think this solution with handle blocks is the easiest to reason about.

Next time, please fill out the help thread template, it helps us a lot to understand the context of what you’re trying to do, and I wouldn’t have had to assume a specific Caddy version that you’re using. Running Caddy in different environments like Docker also brings certain assumptions into play.

2 Likes

Hi, thank you for the quick answer. I am very new with caddy and i just recently downloaded it, i am running this version: (caddy.exe --version)

v2.2.0-rc.1 h1:ULGBB9efRs8yG27IBOMLljhCa4iM1IluFTDxjm+Y8vE=

And i am not running it inside docker.

The solution you posted seems to work great for the reverse_proxy and the 404 handler, but i am still having issues with the file server.

going to localhost/ui/account is returning me the 404 handler (not found)

This example code works:

:1111 {
    root ../accounts/ui/build
    file_server
}

I can see my ui and it works great, but with your code i am getting the 404 handler still. Could you help me with that?

That’s a slightly old version, I recommend using the v2.2.0 stable release.

What exactly is the file structure of your project?

What exactly is the file structure of your project?

I am using a normal react ui that was built using npm run build.

inside the build directory there is the index.html file,the favicon, some images, and then i have a static folder with css, js and media inside of that (a normal react build)

But notice that the file server works great when i use it like this:

:1111 {
    root ../accounts/ui/build
    file_server
}

i can access localhost:1111 and i can see and use my UI from here perfectly fine.
I hope that helps.

As you using the v2.2.0 release like I said? I think it’s likely related to that handle_path fix I mentioned earlier.

Yes, i am using that version now, specifically:

v2.2.0 h1:sMUFqTbVIRlmA8NkFnNt9l7s0e+0gw+7GPIrhty905A=

When i access

localhost/ui/account

in my browser, my url actually changes to localhost now and the webpage says “not found” (seems to be hitting the 404 handler from caddy)

BUT, when i access localhost/ui/account/login then i can see in my network tab that i get a 404 but my screen does NOT say “not found” (maybe this suggests that it tried to redirect but it couldn’t find the file?)

for clarity, this is my caddyfile:

:80 {
	handle_path /ui/account* {
		root * ../accounts/ui/build
		file_server
	}

	handle /api/account* {
		reverse_proxy  localhost:6677
	}

	handle {
		respond "Not found" 404
	}
}

Use curl -v to make the requests, to see exactly what’s happening. Caddy isn’t configured to make any redirects so that must be happening in your react app.

You likely have some base path configuration you need to set before building.

Some reading about this sort of problem:

I can see via curl that there is a redirect, but i am not aware of my app doing any redirects on its own (like i mentioned, serving my files via localhost:1111 works as expected without any redirects)

here is the curl output:

*   Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 80 (#0)
> GET /ui/account HTTP/1.1
> Host: localhost
> User-Agent: curl/7.55.1
> Accept: */*
>
< HTTP/1.1 308 Permanent Redirect
< Content-Type: text/html; charset=utf-8
< Location: /
< Server: Caddy
< Date: Thu, 08 Oct 2020 07:14:05 GMT
< Content-Length: 37
<
<a href="/">Permanent Redirect</a>.

* Connection #0 to host localhost left intact

I have tried making it work on a subdomain account.localhost but that won’t work either.

account.localhost {
    root ../accounts/ui/build
    file_server
}

i am getting a 404 not found error.

I have added this line to my hosts file:
127.0.0.1 account.localhost

Do you have any suggestions for me as to how to structure my caddyfile? I just want to run my two UI’s and my two API’s via caddy, i dont mind using subdomains or whatever to make it work.

Jumping into this late… can you provide your latest, full Caddyfile, along with the exact curl command(s) you’re using? I will try to recreate it and understand what is happening. But first I want to make sure I have the most up-to-date, complete picture.

Sure, let me step back and explain my end goal here first though.
I have multiple services that I want to run, each service has an API (go backend) and a UI (react frontend with react router).

A very big priority for me is ease of development and an easy and uncomplicated transition into production.
All those services (and their frontends) are going to run in their own docker container in production,
so in theory I should be able to run caddy on the linux host directly and then reverse proxy my services (which will run on localhost, exposed via docker)

Preferably, I would just like to run the api’s on /api and the ui’s on /ui while staying on the same domain (in my case localhost)
That would make it easy for me to do redirects based on authentication (a user who is not logged in could be redirected to /ui/account/login for example)

But this seems like it would cause issues, because my react app isn’t set up to run via a subdirectory? I am not sure.

this is the configuration that I used:

:80 {
	handle_path /ui/account* {
		root * ../accounts/ui/build
		file_server
	}

	handle /api/account* {
		reverse_proxy  localhost:6677
	}

	handle {
		respond "Not found" 404
	}
}

the curl command for the output in the previous post above is:

curl -v http://localhost/ui/account

Thank you.

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