Is it possible to serve static files from two different directories?

1. Caddy version (caddy version):

v2.3.0 h1:fnrqJLa3G5vfxcxmOH/+kJOcunPLhSBnjgIvjXV/QTA=

2. How I run Caddy:

I am using caddy in docker.

a. System environment:

pop os 20.04
Docker version 20.10.2, build 2291f61

b. Command:

sudo docker run --rm -it -p 80:80 \
-v `pwd`/Caddyfile:/etc/caddy/Caddyfile \
-v `pwd`/static1:/static1 \
-v `pwd`/static2:/static2 \
caddy

c. Service/unit/compose file:

d. My complete Caddyfile or JSON config:

:80 {
	file_server {
		root /static1
	}

	file_server {
		root /static2
	}
}

3. The problem I’m having:

I’m trying to translate a nginx config file into a caddyfile. Nginx is serving static content from two different directories. This is a minimal and functional nginx config.

The requests would come as /file1, /file2, etc… And this nginx server would go and look for the file in both directories.

events {
	worker_connections 768;
}

http {
    server {
        listen 80;
        listen [::]:80;

        server_name _;

        location / {
            root /static1;
            try_files $uri $uri/ @secondStatic;
        }

        location @secondStatic {
            root /static2;
        }
    }
}

4. Error messages and/or full log output:

Cannot get the files inside the static2 directory. curl -v shows error 404.

5. What I already tried:

I also tried using the try_files directive like this.

try_files /static1{uri} /static2{uri}

file_server {
    root /
}

6. Links to relevant resources:


Please note that I am not in a position to make changes to the directory structure.
Thank you :slight_smile:

Hmm, that’s a strange requirement, but yep, Caddy can do it!

:80 {
	@isInFirst file {
		root /static1
	}
	root @isInFirst /static1
	root * /static2

	file_server
}

This uses a named matcher with the file matcher to check if the requested file exists in /static1; if so, it sets the root to /static1. Else, it’ll set root to /static2.

There’s a few different ways this could be expressed in the Caddyfile, but that’s probably the simplest, if you don’t have any more requirements than that.

2 Likes

That worked. Thanks a lot!

Could you tell me a little bit about the other ways? I’m completely new to caddy, so maybe it might come in handy in the future. The nginx file I’m translating is huge. This was just one part of it.

Typically I’d use handle blocks to group things up, if you need to do anything any more complicated:

:80 {
	@isInFirst file {
		root /static1
	}
	handle @isInFirst {
		root * /static1
		file_server
	}

	@isInSecond file {
		root /static2
	}
	handle @isInSecond {
		root * /static2
		file_server
	}

	handle {
		# Fallback if neither of the first two matched
	}
}

Some additional reading:

2 Likes

That’s neat. One last question, so that I could avoid a mistake in the future.
I’m curious why try_files didn’t work. Was I using it in a wrong way? I tried both {uri} and {path} there.

Caddy’s try_files is a path rewrite. Caddy’s default root is your current working directory. For try_files to work, your root would need to be set to /, but that’s… a terrible idea, for security reasons.

There’s also the difference of the root directive, vs the root subdirective of many directives or the file matcher. The directive sets it “globally” whereas the subdirective is an override to the global one, for that directive.

1 Like

Got it. Thank you :slight_smile:

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