File_serve different directories from different handles?

1. Caddy version (caddy version):

2.4.5

2. How I run Caddy:

Simply caddy run for localhost-only testing right now – no domains.

a. System environment:

Windows 10 pre-compiled executable

b. Command:

caddy run

d. My complete Caddyfile or JSON config:

:8050 {
    handle_path /photos {
        file_server {
            root "d:/photos"
            browse
        }
    }

    handle /videos {
        file_server {
            root "d:/videos"
            browse
        }
    }

    route /songs* {
        file_server {
            root "d:/songs"
            browse
        }
    }

    handle_path /documents* {
        file_server {
            root "d:/documents"
            browse
        }
    }

    handle /books* {
        file_server {
            root "d:/books"
            browse
        }
    }
}

3. The problem I’m having:

I want /photos to be a file_server for the D:/Photos directory, /videos to be a file_server for the D:/Videos directory, and so on.

The above Caddyfile shows me trying different combinations of route, handle, and handle_path, with wildcard and non-wildcard matchers, trying to see which would be appropriate for this situation; none are.

Some of them will show a file_server for the top-level directory, but fail to make subfolders navigable.

5. What I already tried:

Various combinations of handle, handle_path, route, root directives, using :8050/books, :8050/photos etc as entirely different blocks… the only solution so far that’s worked is putting them on different ports.

Path matching in Caddy is exact, so if you use a matcher like /photos, it will only match requests to exactly /photos and not /photos/foo. You must use a * to tell Caddy to match more, i.e. /photos*.

The handle_path directive is the same as handle, except that it strips the matched path prefix from the request before continuing handling. This means that given handle_path /photos* and a request like /photos/foo, the path would become /foo before being handled by file_server.

When file_server looks for files on disk, it takes the root then appends the current request path to it. So for a request /foo, it would look for d:/photos/foo. If you didn’t use handle_path and instead used handle, then the path wouldn’t be stripped, and you’d instead end up with d:/photos/photos/foo.

In this case, route is not helpful for you – it’s purpose is different, it’s meant to override the built-in directive order. The Caddyfile sorts directives based on this order as a “best guess” pass as at generating a working config, so that users are allowed to write directives in whatever order they want. But this sometimes works against the user’s intent, so it’s useful to use route to override this. It’s also useful when using plugins, since plugins aren’t allowed to have a defined directive order, to avoid conflicts between plugins.

All that said, this is probably what you want:

:8050 {
	handle_path /photos* {
		root * D:\photos
		file_server browse
	}

	handle_path /videos* {
		root * D:\videos
		file_server browse
	}

	handle_path /songs* {
		root * D:\songs
		file_server browse
	}

	handle_path /documents* {
		root * D:\documents
		file_server browse
	}

	handle_path /books* {
		root * D:\books
		file_server browse
	}
}
2 Likes