Caddy: Implement dynamic site root

Created on 23 Jun 2016  路  24Comments  路  Source: caddyserver/caddy

Currently, the root directive takes only a single static root path. In other words, a site served as :443 or *.example.com cannot serve from different places on disk. And we can't easily make root dynamic because some directives (like git, log, errors, ...) need to know where the site root is in order to do various things like clone a repo, place log files, etc.

But perhaps we could split the notion of site root and static file root. I'm not yet decided if this violates Caddy's priority of simplicity, but:

root  /path/to/site/root # as before
files /dynamic/{host}    # or maybe 'staticfiles' - overwrites root for static files
files {                  # map multiple site paths
    /subpath1 /sites/site1
    /subpath2 /sites/site2
}
files /bad/method {      # use if conditonals like rewrite and redir have
    if {method} not GET
}

This preserves the vital 'root' path (which defaults to current working directory) but allows dynamic root paths as far as serving static files goes. Note that files or staticfiles _only_ applies to serving static files, nothing else.

Builds on #619 and #167.

What do we think about this?

discussion feature request

Most helpful comment

I'm facing a possibly same problem. I want to create dynamic review urls (*.review.app.tld) and it would be awesome if i could use {hostonly} inside the root directive...

All 24 comments

This should be a plugin. I think it's too complex for Caddy core and I doubt it's common enough use case to justify.

I say this as someone who wants to do something similar.

The more I think about this, the more it looks like what rewrite does.

Good point. I suppose you could allow vars in rewrite and then do something like:

root /var/www
rewrite / /{host}

(This is actually what I tried when I wanted to do this.)

I'm a newcomer to Caddy, so these two cents may reveal ignorance of the priority of simplicity (despite having read #619 and #167). The {host} seems rather elegant, allowing the form of 麓sites/{host}麓 so for example a virtual host of *.somewhere.dev could easily serve sites/test.somewhere.dev and sites/test2.somewhere.dev with very little setup.

@jahands Did you have any success in using rewrite for this? My aim was just to apply a set of directives necessary for correctly serving files according to a CMS to a dynamic set of subdomains, essentially the aforementioned example.

I wonder if I have have complicated this unnecessarily, could I perhaps serve the files from a subdomain (still with shared directives) with only the wildcard virtual host? That is, a virtual host of *.somewhere.dev would already serve files from somewhere.dev/test as test.somewhere.dev - but settings applied to *.somewhere.dev will also apply to subdomains?

@jahands Did you have any success in using rewrite for this?

No, because rewrite doesn't currently support variables. I think maybe it should though since it would allow this behavior without complicating things too much or adding a new directive.

I also think it makes sense to reduce this feature down to rewrite, and I don't (at the moment) see why it can't support placeholders in the to values. (/cc @abiosoft) Here's my reasoning.

A site root is where a site is based. If a site is in two or more folders, then those folders still have a shared ancestor if you go up the tree enough. For example, /a/b/c/site1 and /a/d/e/site2 share /a, which is thus the site root. If the git directive is cloning a repository, git clones the repository into one folder only. But that repository can have subfolders, and those subfolders may contain 'different' sites.

So if you want to serve static files out of different folders for example.com/a and example.com/b requests, then you can do this with Caddy 0.9:

example.com/a {
    root /sites/siteA
}

example.com/b {
    root /sites/siteB
}

But if you want those two sites to have everything in common except for their root, it would be nice if this was possible with rewrite or with a new directive like I proposed above. (Currently, ext is a specific kind of rewrite that's common; perhaps we could have another directive like above that's also a specific kind of rewrite.)

to supports placeholders

That's what I thought. So maybe this is a non-issue?

I didn't realize that. I must have done something wrong before.
Looks like there is no need for changes then.

It is in the docs https://caddyserver.com/docs/rewrite

... is one or more space-separated paths to rewrite to, with support for request placeholders as well as numbered regular expression captures such as {1}, {2}, etc.

So it becomes

rewrite / {
    to /{host}
}

That's pretty reasonable I think.

We do have ext which is a specific, common type of rewrite. Maybe we could make a new directive for this type of rewrite, if it's common?

I always assumed that the same mechanic was what Apache used in .htaccess to share configurations between wildcard subdomains.

Well, I guess if this comes up again, we can look at it, but for now it seems that the rewrite I posted above should work fine for that particular case.

@mholt, is this then the required way of doing it?

toastview.hazcod.com {
    header / {
        "Strict-Transport-Security" "max-age=63072000; includeSubdomains; preload"
        "X-Frame-Options" "DENY"
        "X-Content-Type-Options" "nosniff"
        "X-XSS-Protection" "1; mode=block"
        "Access-Control-Allow-Origin" "{host}"
        "Access-Control-Allow-Methods" "GET, POST, OPTIONS"
        -Server
    }

    gzip

    fastcgi / php-fpm:8080 php
}
toastview.hazcod.com/css {
    header / -Server

    root /assets/css
    gzip
}
toastview.hazcod.com/js {
    header / -Server

    root /assets/js
    gzip
}
toastview.hazcod.com/img {
    header / -Server

    root /assets/img
    gzip
}
toastview.hazcod.com/fonts {
    header / -Server

    root /assets/fonts
    gzip
}

@HazCod You could do that, or you could set a common root and use rewrite to adjust the paths that way. I like your way better than this alternative, however, because it's much clearer and explicit.

@mholt; for completeness sake;
This does not work, because I did not set a correct root.

hazcod.com {
    header / {
        "Strict-Transport-Security" "max-age=63072000; includeSubdomains; preload"
        "X-Frame-Options" "DENY"
        "X-Content-Type-Options" "nosniff"
        "X-XSS-Protection" "1; mode=block"
        "Access-Control-Allow-Origin" "{host}"
        "Access-Control-Allow-Methods" "GET, POST, OPTIONS"
        -Server
    }

    gzip
    rewrite {
        ext .css .png .jpg .ico .js
        if {path} match ^(/js/|/img/|/fonts/|/css/|)
        to /assets/{path}
    }

    fastcgi / php-fpm:8080 php
}

This does work, but allows any file in /assets/ to be queried and will try finding files on the fs instead of checking the path.

hazcod.com {
    header / {
        "Strict-Transport-Security" "max-age=63072000; includeSubdomains; preload"
        "X-Frame-Options" "DENY"
        "X-Content-Type-Options" "nosniff"
        "X-XSS-Protection" "1; mode=block"
        "Access-Control-Allow-Origin" "{host}"
        "Access-Control-Allow-Methods" "GET, POST, OPTIONS"
        -Server
    }

    gzip
    root /assets/

    fastcgi / php-fpm:8080 php
}

Or even better;

hazcod.com {
    header / {
        "Strict-Transport-Security" "max-age=63072000; includeSubdomains; preload"
        "X-Frame-Options" "DENY"
        "X-Content-Type-Options" "nosniff"
        "X-XSS-Protection" "1; mode=block"
        "Access-Control-Allow-Origin" "{host}"
        "Access-Control-Allow-Methods" "GET, POST, OPTIONS"
        -Server
    }

    gzip

    fastcgi / php-fpm:8080 php {
        except /css/,/js/,/img/,/fonts/
    }

    root /assets/
}

I'm facing a possibly same problem. I want to create dynamic review urls (*.review.app.tld) and it would be awesome if i could use {hostonly} inside the root directive...

Hi, sorry to bring this up again, but I'm trying to do the following and find myself in need of this feature :)

I have a directory with static sites whose domains I don't know, so it would be awesome to just do this:

# using http:// just for local testing
http:// {
  root /path/to/sites/{hostonly}
}

This would be the same as creating a block (sorry if I don't manage the caddy jargon yet) for every site and it would trailing slash canonicalization (301 host => host/, etc.) which I tested it's what caddy does by default.

So, following what has been discussed here I arrived at the following:

http:// {
  root /path/to/sites

  rewrite {
    to {hostonly}{path}/ {hostonly}{path}
  }
}

But I'm not getting trailing slash canonicalization, so http://host and http://host/ both work but the first break relative urls, and also http://host/index.html solves to http://host/host/index.html.

Is there a way to achieve what I want? The examples given here lead to loop redirection (for instance https://github.com/caddyserver/caddy/issues/901#issuecomment-228184775)

@fauno Please try Caddy 2, it supports dynamic file server roots. https://github.com/caddyserver/caddy/tree/v2

@mholt

dynamic (wildcard) root directive is still not possible in 2.2.* caddy, or am I missing something?

Example what I think:
https://*.domain.tld {
root * /home/pee/stages/{host}
php_fastcgi unix//var/run/php/php74/domain.sock
}

Do you have it on roadmap, or this wont be possible?

Thanks for great software.

@phrazer Try asking on our forum and fill out the help template, thanks! https://caddy.community

@phrazer To clarify, this closed issue is a year old, and the forum is what we use for questions related to usage or help, and it has a help template designed for that. It will help us understand your question/problem better.

I asked you a simple question, it would take you less time to just answer - is there a 'dynamic site root' or if not is it on roadmap?

Can we have {host} in root * /home/{host} path for wildcards or not?

Belive me i checked everything and reading docs for 2 days. I also wrote simple config so you get the idea right away.

Thanks for great software, i wont bother you any more.

I already did answer above:

it supports dynamic file server roots.

So yes, it is supported, which is why we need more information to help you, and it is not fair (good manners) to the other participants of the thread to take over /revive something that was closed a year ago (and opened 4 years ago!) with a help/usage question. If you go to the forum we can help you more there. I'm sure the other participants don't want to keep getting notifications from our conversation.

Was this page helpful?
0 / 5 - 0 ratings