File_servers on different paths not working

1. Caddy version (caddy version):


2. How I run Caddy:

a. System environment:

Linux (dev) & Docker (both don’t work)

b. Command:

caddy run -config

c. Service/unit/compose file:

FROM caddy

COPY Caddyfile /etc/caddy/Caddyfile

COPY . /data

d. My complete Caddyfile or JSON config:

:29016 {
  root * /home/arcoreese/root-ca-downloader
  file_server /g* {
  file_server {
    index ArmstrongsCA.crt

I use port 80 in production instead of a dev high port

3. The problem I’m having:

When I go to /g it gives me a 404 in chrome and a blank screen in firefox. But when I go to the root (/) it works fine and shows me the cert. I also put the browse at the root and it works.

4. Error messages and/or full log output:

2021/03/03 17:26:10.210	INFO	using provided configuration	{"config_file": "", "config_adapter": ""}
2021/03/03 17:26:10.215	INFO	admin	admin endpoint started	{"address": "tcp/localhost:2019", "enforce_origin": false, "origins": ["localhost:2019", "[::1]:2019", ""]}
2021/03/03 17:26:10.217	INFO	tls.cache.maintenance	started background certificate maintenance	{"cache": "0xc00045db20"}
2021/03/03 17:26:10.220	INFO	tls	cleaned up storage units
2021/03/03 17:26:10.220	INFO	autosaved config	{"file": "/home/arcoreese/.config/caddy/autosave.json"}
2021/03/03 17:26:10.220	INFO	serving initial configuration

5. What I already tried:

Wrapping the file_servers in handles.
Using /g instead of a wildcard /g*
Putting the browser at the root works fine

6. Links to relevant resources:

Here’s a video showing the problem:

I don’t think this is what you want to do in your Dockerfile. /data is a directory that Caddy uses for persisting important files. A better place to put your files is /srv. Make sure you’re persisting /data with a volume. See Docker Hub

I assume this is when you’re running Caddy outside of Docker (because I don’t see a volume mount for that directory). Do you have a directory /g inside of /home/arcoreese/root-ca-downloader?

Caddy appends the current request path to the defined root when looking for files. If you don’t have a /g directory and only want that to be the path, then you’ll want to use handle_path to strip the path prefix before handling the request with file_server:

root * /home/arcoreese/root-ca-downloader

handle_path /g* {
	file_server browse

file_server {
	index ArmstrongsCA.crt
1 Like

Yes, I use /data instead of my home folder. I will change it to /srv, didnt know that. Also, because the data never changes, its static, I don’t persist with a volume and build it into the image. I don’t have a directory /g, it is just supposed to be a web url path to get to a browse. When I go to /g it just redirects to the root and doesn’t browse.

That’s fine – I mean you should persist /data because that’s important state for Caddy that should be kept across container starts, i.e. certificates and keys.

Right, then the solution is handle_path as I suggested above. Did you try it?

The redirecting is from handle_path. Forgot to mention that.

What do you see when you make the request with curl -v? What does the redirect look like?

curl -v

*   Trying
* Connected to ( port 29016 (#0)
> GET /g HTTP/1.1
> Host:
> User-Agent: curl/7.75.0
> Accept: */*
* Mark bundle as not supporting multiuse
< HTTP/1.1 301 Moved Permanently
< Content-Type: text/html; charset=utf-8
< Location: /
< Server: Caddy
< Date: Thu, 04 Mar 2021 12:57:27 GMT
< Content-Length: 36
<a href="/">Moved Permanently</a>.

* Connection #0 to host left intact


Sorry, I think there’s a bug here in that the file_server browse handler doesn’t behave well when in handle_path because of the path stripping logic which makes the redirects misbehave. I was hoping to get some help from @matt about this but he’s been quite busy lately.

I think as a workaround, you could make a symlink to effectively do the same thing – make a symlink from g to .. (i.e. one directory up) inside of your root. Then this should work:

file_server /g* browse

My bad. I think these two redirect() calls need to use the “original URL” stored in the request context, instead of the current (rewritten) URL:

(Like this: originalReq, _ := req.Context().Value(OriginalRequestCtxKey).(http.Request))

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