V2: Try file server first?

I have a single server with static files that I’d like Caddy to serve along with the reverse_proxy. The caddy file looks like this.

domain.com {
    root * /src/public
    file_server
    reverse_proxy / localhost:3000
}

This works fine, but Caddy will never serve the files because the reverse proxy matches everything. Is there a way to try the file server first, or do I have to write matchers for the reverse_proxy?

Thanks for trying Caddy 2!

That’s right – the file server and the reverse proxy are both terminal handlers, meaning they don’t invoke the next handler in the chain if they both match a request, and the reverse proxy is coded to come first by default when using the Caddyfile.

Right now, they match all requests (because all request paths start with /). When do you want the file server to be used instead? Caddy doesn’t know what you want to do since you told it to use both the file server and the reverse proxy for all requests, so it just picked the reverse proxy to go first.

You have a couple options off the bat:

  1. You can restrict either the file server or the reverse proxy to match only certain requests.

  2. You can change the order of the directives, using handler_order option in the global config: https://github.com/caddyserver/caddy/wiki/v2:-Documentation#global-config – you can change the order to that of appearance in the Caddyfile or hard-code your own order for the directives.

Option 1 will tell Caddy to use the file server or the reverse proxy depending on the request, whereas option 2 (without any other changes) will let you tell Caddy to use the file server instead of the reverse proxy when they both match the same request; but that makes having both in the Caddyfile redundant still.

So I think what you want to do is tell Caddy to use one or the other depending on the request, not to use both for all requests (because it can only use 1). Does that make sense?

That makes sense, thanks for the info. I think it would be awesome if the caddy fileserver would take priority for requests that match files in the root, and then hand off to the reverse proxy for those that don’t.

Here’s the matcher that I came up with for avoiding static assets with a webpacker rails app in case anyone else needs it

matcher notStatic {
    not {
        path /assets/* /packs/js/* /404.html /422.html /500.html /robots.txt
    }
}

This works, but I was confused why all of the paths have to be on the same line. For example the following will not work.

matcher notStatic {
    not {
        path /assets/* 
        path /packs/js/* 
        path /404.html
        path /422.html
        path /500.html
        path /robots.txt
    }
}

But according to the docs matchers in the same group are anded so it should work the same, no?

1 Like

@sosodev Aha, I made some progress on this.

First I fixed a bug related to matcher tokens. That’s pushed in https://github.com/caddyserver/caddy/commit/263ffbfaecc5ed8b7f5071baecf51b4e9d90e7bf. Discovered while investigating your question but not relevant in the final solution below.

Then I found a much easier way to do what you want without needing any changes. :slight_smile:

If your goal is to simply trigger the reverse proxy if the request does not match a static file on disk:

matcher notStatic {
    not {
		file {
			try_files {path}
		}
	}
}
reverse_proxy match:notStatic localhost:3000
file_server

Tada!

With this configuration, the try_files option for the file matcher will match the request if any of the given files exist. So it simply tries the request path within the root you specified, and if it doesn’t exist, it will not match – but since we’re in a “not” matcher, it negates that and will match!

So you can use that with the reverse proxy, without needing to specify each static file. :slight_smile:

Case closed?

1 Like

Tested and that definitely does the trick! Very cool stuff. Thanks for your help Matt!

1 Like

No problem, thanks again for using Caddy 2!

I do like that solution a lot, you don’t need to enumerate all the files to match.

1 Like