Simple file server config is returning an empty response

1. Caddy version (caddy version): v2.4.6

2. How I run Caddy:

a. System environment: Arch Linux, direct execution from shell

b. Command:

caddy run

c. Service/unit/compose file:

N/A

d. My complete Caddyfile or JSON config:

{
	http_port 2015
}

http://localhost

file_server /doc/* browse {
	root /usr/share/doc
}

3. The problem I’m having:

Expectation: List of contents under the root of the file_server

Actual result: Empty response:

> curl -v 'http://localhost:2015/doc'
*   Trying 127.0.0.1:2015...
* Connected to localhost (127.0.0.1) port 2015 (#0)
> GET /doc HTTP/1.1
> Host: localhost:2015
> User-Agent: curl/7.81.0
> Accept: */*
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Server: Caddy
< Date: Sat, 22 Jan 2022 16:36:33 GMT
< Content-Length: 0
< 
* Connection #0 to host localhost left intact

4. Error messages and/or full log output:

> caddy run
2022/01/22 16:36:05.597	INFO	using adjacent Caddyfile
2022/01/22 16:36:05.600	INFO	admin	admin endpoint started	{"address": "tcp/localhost:2019", "enforce_origin": false, "origins": ["localhost:2019", "[::1]:2019", "127.0.0.1:2019"]}
2022/01/22 16:36:05.600	INFO	http	server is listening only on the HTTP port, so no automatic HTTPS will be applied to this server	{"server_name": "srv0", "http_port": 2015}
2022/01/22 16:36:05.600	INFO	tls.cache.maintenance	started background certificate maintenance	{"cache": "0xc000480000"}
2022/01/22 16:36:05.600	INFO	tls	cleaning storage unit	{"description": "FileStorage:/home/TheDcoder/.local/share/caddy"}
2022/01/22 16:36:05.601	INFO	tls	finished cleaning storage units
2022/01/22 16:36:05.601	INFO	autosaved config (load with --resume flag)	{"file": "/home/TheDcoder/.local/share/caddy/autosave.json"}
2022/01/22 16:36:05.601	INFO	serving initial configuration

5. What I already tried:

Reading lots of documentation but I couldn’t figure out the issue. I am sure I am missing something very obvious…

6. Links to relevant resources:

Your path matcher /doc/* does not match the request /doc because it’s missing the trailing slash. You can change it to /doc* so that it matches.

You’ve only configured Caddy to handle requests to that path, so anything else will return an empty response – Caddy worked as it was configured, i.e. to do nothing.

Also, keep in mind that the file server works by taking the defined root and appending the request path to it. So for a request like /doc/index.html, your root of /usr/share/doc will get joined with the path and Caddy will look for the file at /usr/share/doc/doc/index.html. To fix this, you can use the handle_path directive which will strip the given path prefix before running the handlers within.

2 Likes

Thank you for the detailed response!

I have updated my configuration to match both /doc/* and /doc, but I am still having a bit of trouble getting the file_server to work:

{
	http_port 2015
}

http://localhost

@doc {
	path /doc /doc/*
}

handle @doc {
	#respond "It works!"

	uri strip_prefix /doc

	file_server browse {
		root /usr/share/doc
	}
}

(I used handle instead of handle_path as the latter doesn’t support named matchers)

I tested that the path is being matched correctly with a response handler, but the file_server still doesn’t seem to be working, not sure what’s wrong this time, I get the same empty response :confused:

Try this:

http://localhost {
	handle_path /doc* {
		root * /usr/share/doc
		file_server browse
	}
}

You can also turn on the debug global option to see additional information in the logs.

I updated my config again to roughly match your config:

{
	debug
	http_port 2015
}

http://localhost

@doc {
	path /doc /doc/*
}

handle @doc {
	uri strip_prefix /doc

	root * /usr/share/doc

	file_server browse
}

And I am now getting a 404 Not Found status :thinking:

> curl -v 'http://localhost:2015/doc'
*   Trying 127.0.0.1:2015...
* Connected to localhost (127.0.0.1) port 2015 (#0)
> GET /doc HTTP/1.1
> Host: localhost:2015
> User-Agent: curl/7.81.0
> Accept: */*
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 404 Not Found
< Server: Caddy
< Date: Sat, 22 Jan 2022 19:58:53 GMT
< Content-Length: 0
< 
* Connection #0 to host localhost left intact

The interesting thing is that the DEBUG log explicitly states that it is “listing directory content:exploding_head:

2022/01/22 20:02:23.311	DEBUG	http.handlers.rewrite	rewrote request	{"request": {"remote_addr": "127.0.0.1:57260", "proto": "HTTP/1.1", "method": "GET", "host": "localhost:2015", "uri": "/doc", "headers": {"User-Agent": ["curl/7.81.0"], "Accept": ["*/*"]}}, "method": "GET", "uri": "/"}
2022/01/22 20:02:23.311	DEBUG	http.handlers.file_server	sanitized path join	{"site_root": "/usr/share/doc", "request_path": "", "result": "/usr/share/doc"}
2022/01/22 20:02:23.311	DEBUG	http.handlers.file_server	no index file in directory	{"path": "/usr/share/doc", "index_filenames": ["index.html", "index.txt"]}
2022/01/22 20:02:23.311	DEBUG	http.handlers.file_server	browse enabled; listing directory contents	{"path": "/usr/share/doc", "root": "/usr/share/doc"}

P.S The reason why I have a complex configuration is because I am testing several scenarios and this is the minimal configuration I could make which reproduces the issue I am encountering. For example I will be using different site handlers so I can’t simply use a global file_server.

That’s really strange :thinking:

You seem to be having the same issue as the person in this topic:

You’re running on Arch as opposed to that user running in Docker though.

Could you try building Caddy from the branch I mentioned at the end of that topic, and try that? You can build it with xcaddy, using this command:

$ xcaddy build browse-errors
2 Likes

Your branch fixed the issue with directory listing :smile:

Addendum: Out of curiosity I tested the same configuration on my ARMv7 Pi with the caddy package from Arch Linux ARM repository and it works out of the box, so the issue might be something related to how it’s being built by certain distributors.


Now I just need to figure out why the paths are relative to the site root (localhost:2015/foo) instead of the /doc handle (localhost:2015/doc/foo)… perhaps uri strip_prefix shouldn’t be used here?

How do I serve a file_server with an arbitrary root on a certain handler/path?

So, we found in a github issue that the problem had just already been fixed on the master branch, a fix that hasn’t been in a release yet.

I think you’re running into this bug:

Essentially, you need to make sure that requests to /doc get redirected to /doc/ for it to work. Using uri strip_prefix is currently preventing that from happening. So you’ll need to add another line to your config:

redir /doc /doc/
1 Like

Ah, good. Can you link to the issue here for reference so that those who are curious like me can check it out?

Wow, didn’t think I would bump into 2 bugs with my tests, good to know that I wasn’t going crazy trying to figure out what the issue is in my config :smile:

Anyway, redirecting to the path ending with slash did fix the canonicalization issue, here’s my final configuration which is working:

{
	debug
	http_port 2015
}

http://localhost

@doc {
	path /doc /doc/*
}

handle @doc {
	uri strip_prefix /doc
	
	redir /doc /doc/

	file_server browse {
		root /usr/share/doc
	}
}

I guess this issue can be considered “solved” but I am unsure about which post to pick as the solution.

1 Like

Unrelated to this topic, but I think this bug is not limited to file_server, I experience the same issue with php_fastcgi where it indirectly redirects to index.php on the upper path when the last / isn’t supplied in the URL, redir does fix the issue.

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