Caddy Fails to serve static files

1. Caddy version (caddy version):

v2.4.3 h1:Y1FaV2N4WO3rBqxSYA8UZsZTQdN+PwcoOcAiZTM8C0I=

2. How I run Caddy:

caddy run

a. System environment:


b. Command:

caddy run

c. Service/unit/compose file:

Paste full file contents here.
Make sure backticks stay on their own lines,
and the post looks nice in the preview pane.

d. My complete Caddyfile or JSON config:


file_server /static/* {
    root ./

log {
    output file log.txt

reverse_proxy /* http://localhost:5000

3. The problem I’m having:

I believe I’m missing some critical piece of information. I’m unable to get a Python Flask app to behave right. I just need some Caddy Guru to please review my config and tell me what I’m doing wrong. I can’t get the reverse_proxy and the file_server directive to play nicely. I need the static resources to be handled by caddy while every other path is handled by reverse proxy but caddy file_server shows 404 for files that are right there and that i can see using the file_server browse command.
My apologies if i’m not following the help template correctly. Its my first time here.

4. Error messages and/or full log output:

2021/08/06 18:56:16.863 error http.log.access.log0 handled request {“request”: {“remote_addr”: “[::1]:63076”, “proto”: “HTTP/1.1”, “method”: “GET”, “host”: “localhost:3333”, “uri”: “/static/images/member/4.jpg”, “headers”: {“Sec-Fetch-Dest”: [“document”], “Cache-Control”: [“max-age=0”], “Sec-Fetch-User”: ["?1"], “User-Agent”: [“Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36”], “Accept-Encoding”: [“gzip, deflate, br”], “Accept-Language”: [“en-US,en;q=0.9”], “Upgrade-Insecure-Requests”: [“1”], “Sec-Fetch-Site”: [“none”], “Sec-Fetch-Mode”: [“navigate”], “Sec-Ch-Ua”: ["“Chromium”;v=“92”, " Not A;Brand";v=“99”, “Google Chrome”;v=“92"”], “Sec-Ch-Ua-Mobile”: ["?0"], “Connection”: [“keep-alive”], “Accept”: [“text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,/;q=0.8,application/signed-exchange;v=b3;q=0.9”]}}, “common_log”: “::1 - - [06/Aug/2021:23:56:16 +0500] “GET /static/images/member/4.jpg HTTP/1.1” 404 232”, “duration”: 0.002144, “size”: 232, “status”: 404, “resp_headers”: {“Server”: [“Caddy”, “waitress”], “Content-Length”: [“232”], “Content-Type”: [“text/html; charset=utf-8”], “Date”: [“Fri, 06 Aug 2021 18:56:16 GMT”]}}

5. What I already tried:

I have tried different variations of the reverse_proxy and file_server directive and I’ve changed file paths inside my html files in order to get them to find the files. I’ve had mixed results e.g sometimes the file_server works correctly and the reverse_proxy fails whereas with other configs when the reverse_proxy works correctly the static resources are not found by caddy.

6. Links to relevant resources:

What does your file structure on disk look like?

Caddy takes the configured root path and appends the request path to it when looking for files on disk.

Do you have a /static directory on disk? If not, you’ll need to use uri strip_prefix /static to remove that from the request path before file_server looks for files.

Thanks for responding. Yes i do have a static directory I think the issue here is that when I enable reverse_proxy it acts as a catch-all and doesn’t let the request go through to file_server . I think I’ll just let the backend API take care of all the static stuff and use caddy in the front just for https and compression.

Ah, yeah you’re right.

Caddy sorts directives according to this predetermined directive order, but sometimes it’s not optimal for every usecase.

To solve your problem, you may use handle blocks to wrap your directives. Try something like this:

handle /static* {
    root ./

handle {
    reverse_proxy http://localhost:5000

Thank you very much. That seems to have solved the issue. Can you please explain how handle changed things in this case.

1 Like

The handle directive lets you define mutually exclusive request handling branches. This means that if one is matched, no other handle will execute; only the first matched handle will run.

The Caddyfile adapter will sort the handle blocks in order of most specific request matcher to the least specific. The one with a path matcher is more specific than the one without a matcher, because no matcher means “match all requests”, so that should be tried last.

Essentially we’re taking advantage of the sorting behaviour of handle blocks to make sure the directives run in the order we want.

For a deeper dive, you can read this wiki article:


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