V2: How do I selectively rewrite multiple times

1. Caddy version (caddy version):

v2.1.1 h1:X9k1+ehZPYYrSqBvf/ocUgdLSRIuiNiMo7CvyGUQKeA=

2. How I run Caddy:

I run Caddy in a multi-container docker-compose setup.

a. System environment:

Ubuntu 20.04, Docker 19.03.13

b. Command:

docker-compose up proxy

c. Service/unit/compose file:

    image: caddy:2-alpine
    restart: always
      - 80:80
      - 443:443
      - caddy_data:/data
      - caddy_config:/config
      - ./Caddyfile:/etc/caddy/Caddyfile
      driver: syslog
        tag: caddy

d. My complete Caddyfile or JSON config:

static.example.com {
  reverse_proxy minio:9000
  route expression {uri}.endsWith("/") {
  	rewrite {uri}index.html
  rewrite * /static/{uri}

3. The problem I’m having:

I would like to proxy a MinIO installation that contains a static website
in a public bucket called static. MinIO does not know how to serve static websites
(i.e. serving index.html) so I would like to rewrite all /path/to/something/
requests to /static/path/to/something/index.html while everything else
gets rewritten to /static/{uri} without appending index.html.

From what I understand my rules from my Caddyfile should apply one rewrite for
an asset like https://static.example.com/script.js and two rewrites for
https://static.example.com/blog/. However, it looks as if both rewrites get
applied to all requests as https://static.example.com/blog/ returns the correct
index document when https://static.example.com/script.js does return 404. Both
resources exist in the bucket.

When I remove the route block from my config, I can access
https://static.example.com/script.js, but https://static.example.com/blog/
will 404.

4. Error messages and/or full log output:


5. What I already tried:

Using the given Caddyfile. I also tried using path_regexp matcher with the same

6. Links to relevant resources:

This is a Caddy v1 issue on the same topic ecosystem: Add a caddy plugin to serve static files from Minio server. · Issue #3161 · minio/minio · GitHub

I figured out what to do myself, the “expression” matcher needs to be a named one:

static.example.com {
  reverse_proxy minio:9000
  @document {
    expression {uri}.endsWith("/")
  route @document {
    rewrite {uri}index.html
  rewrite * /static/{uri}

While this makes sense, it’s kind of confusing that passing the expression matcher as-is makes Caddy apply it to everything (instead of nothing or not even starting up).

Glad you figured it out!

Hmm, yeah I think the route directive parser doesn’t reject non-matcher arguments, and it probably should. I’ll look into making this a bit stricter.

The reason it matches everything is because your expression arguments get completely ignored, so it’s the same as if you just used route without matchers.

FYI, you can shorten your matcher like this (single-line named matcher syntax):

@document expression {uri}.endsWith("/")

But also, you can just use a path matcher like path */ instead. {uri} will include the query portion, which I’m not sure is what you want to do here.

FYI, the directive order will make your /static/{uri} rewrite apply before the one you wrapped in a route, due to the Caddyfile directive order. Is that what you intended?

You could avoid the route altogether if you meant it the other way around, by putting the matcher on rewrite:

rewrite @document {uri}index.html
rewrite * /static/{uri}

FYI I just wrote a PR to disallow things like route expression so that you’re told that you did something wrong :smile:


Thank you, this is much appreciated and will hopefully prevent some future headscratching.

1 Like

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