[Solved] Redirect locally non existing files to another domain using part of uri

I have a Caddyfile which has default rewrite configured:

rewrite {
      to {path} {path}/ /index.php?{query}
}

Now I want to redirect requests to a directory to another server if the file is not available local.
My current testing config looks like this:

   # use previous behaviour if not a request to the resources directory
   rewrite {
      if {path} not_match ^\/resources
      to {path} {path}/ /index.php?{query}
    }
    # extract the last part of the resources directory if the file doesnt exist
    rewrite {
        r ^/resources/(.*)$
        to {path} /{1}
    }
    # finally redirect requests to /resources using the rewrite-uri
    redir {
       # Modify the destination and status as required
       /resources https://media.mycdn.org/{rewrite-uri} 307
    }

The result is, that files which are available in my local resources directory are served correctly, but non existing files don’t redirect, but return 404.
I tried a few configs, but I dont see how the rewrite/redir play together here.

Hmm. At a guess I’d say it’s because you’re actually rewriting to remove the /resources/ prefix when the file’s not found.

Actually, at a glance, I’d expect that a request for a file inside /resources/ that exists locally would actually be redirected instead of served, with that setup. You’re definitely getting 200s and the file served locally in this case?

Anyway, you can’t really use a base path that you’re expecting to rewrite away from as the base path for your redir. You’ll need to use a blanket redir (because the rewrite could go anywhere), and you can use if to check whether it was rewritten away from /resources/.

I’ve written a few of these before, but this is an example by hand and untested. A few tips for the other components, too.

rewrite {
  to {path} {path}/ /?{query}          # canonical reference to index
}

rewrite /resources/ {                  # use base path to avoid running
  r ^/resources/(.*)$                  # regex on every single request -
  to {path} /{1}                       # longer base path rewrite also
}                                      # wins, no need to exclude above

redir {
  if {uri} not {rewrite_uri}           # check we did rewrite the URI
  if {uri} starts_with /resources/     # specifically, from /resources
  / https://media.mycdn.org/{rewrite-uri} 307
}

Many thanks!
I finally got it running with a few changes.

  1. rewrite-uri must be changed to rewrite_uri
  2. I needed to move the fallback rewrite right at the bottom of the config
  3. The rewrite /resources/ { was ignored - thus I changed rewrite {
  4. I removed the slash from /{1} since it was leading to a double slash in the redirect
    rewrite {                  # use base path to avoid running
        r ^/resources/(.*)$                  # regex on every single request -
        to {path} {1}                       # longer base path rewrite also
    }                                      # wins, no need to exclude above

    redir {
        if {uri} not {rewrite_uri}           # check we did rewrite the URI
        if {uri} starts_with /resources/     # specifically, from /resources
        / https://media.mycdn.org/{rewrite_uri} 307
    }

    rewrite {
        to {path} {path}/ /index.php?{query}
    }
1 Like

Good catch on the first one!

The second one is a direct result of the third. When one has a longer base path, it automatically “wins” out of the two, but with identical base paths (the implicit /), ordering becomes important. I’m not sure why it would be ignored, though. Maybe /resources would’ve been a better base path than /resources/?

As for the fourth, I don’t think it’s actually the slash before {1} that’s important. It’s the slash in the middle of media.mycdn.org/{rewrite_uri}, since {rewrite_uri} comes with the prefixed slash. It all comes out in the wash, though.

Glad it’s working for you.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.