Caddy prefers proxy directive over matching static files

I’m running Caddy 0.9.5 in a container from the docker image abiosoft/caddy alongside a nodejs container.

I want to serve static files from Caddy and proxy any requests that don’t match a file to another docker container.

This is my Caddyfile:

0.0.0.0
root /srv/build/
log / stdout
proxy / my_node_service

If I comment out the proxy directive and request a file, I see Caddy serving the file:

HTTP/1.1 200 OK
Accept-Ranges: bytes
Content-Length: 30980
Content-Type: image/png
Etag: W/"58f69673-7904"
Last-Modified: Tue, 18 Apr 2017 22:42:59 GMT
Server: Caddy
Date: Tue, 18 Apr 2017 23:35:59 GMT

But when I uncomment the proxy directive, Express serves the file:

HTTP/1.1 200 OK
Accept-Ranges: bytes
Cache-Control: public, max-age=0
Content-Length: 30980
Content-Type: image/png
Date: Tue, 18 Apr 2017 23:34:49 GMT
Etag: W/"7904-15b833bb138"
Last-Modified: Tue, 18 Apr 2017 22:42:59 GMT
Server: Caddy
X-Powered-By: Express

The www path for that file is /static/media/member-bg.3c556b08.png and the server path is /srv/build/static/media/member-bg.3c556b08.png.

All of the files I need to serve live in /srv/build/static/ and should be served from /static/.

You can get around this some creative use of rewrite.

The following untested Caddyfile should achieve what you’re looking for:

0.0.0.0
root /srv/build
log / stdout
proxy /proxy my_node_service {
    without /proxy
}
rewrite {
    to {path} {path}/ /proxy{path}
}

It will try a file, if possible, then a folder, then it will pass the request to /proxy, but it won’t send /proxy upstream (to avoid confusing your backend node service).

So the rewrite happens after path matching for static files but before path matching for proxy?

Can you please clarify what your rewrite directive is supposed to do? The docs don’t show rewrite taking 3 args like that and Caddy complains with

Parse error: Wrong argument count or unexpected line ending after '{path}'

. I did try it with rewrite {path} /proxy/{path} but only the home page would load. All other pages Caddy returned a 404 rather than proxying to my backend. I also tried moving the rewrite directive above the proxy directive (does that matter?).

My Caddyfile now looks like this:

0.0.0.0
root /srv/build/
log / stdout
rewrite {path} /proxy/{path}
proxy /proxy backend {
    without /proxy
}

# Syntax Error below:
#rewrite {
#   {path} {path}/ /proxy{path}
#}

My bad, I’ve used the wrong syntax! Replace with:

rewrite {
    to {path} {path}/ /proxy{path}
}

Now by way of explanation: there is an implicit base path of / after the rewrite directive but before the block. That means that all requests will be affected by this rewrite.

The to subdirective specifies, in order, which resources Caddy should attempt to serve. Firstly, we try {path}, which will resolve to a file if one exists, or move on if not. Next is {path}/, which will try a folder, if one exists. Lastly, we send it to /proxy{path} - which will treat a request for /foo (as an example) as a request for /proxy/foo.

Since anything at /proxy is being proxied to your node path, Caddy will always stop rewriting at this point (because it can always try this resource). In our proxy directive block, we use without to subtract /proxy from the path heading upstream, so that our node instance will receive the request for /foo instead of /proxy/foo (which will obviously not behave unless you’re expecting it).

2 Likes

Thanks for the help. It looks like it’s working great now.

Very helpful write-up and explanation.

After reading the docs a bit more, I found a simpler solution is to use except in conjunction with proxy:

proxy / backend {
    except /static
    transparent
}
2 Likes

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.