Redirect based on folder if host

Caddy is doing a great job of making me feel udderly incompetent :sweat_smile:

How can I redirect based on the path. My goal is this

http?://<notpear.test.com><port>/pear/??? -> http?://pear.test.com:<port>/pear/???
http?://<notapple.test.com><port>/apple/??? -> http?://apple.test.com:<port>/apple/???

In other words if you try to access the pear folder and you’re not coming from the pear domain then redir to the pear domain. Similarly for apple folder and domain

http://test.com/pear/  -> http://pear.test.com/pear/
http://test.com/pear/foo -> http://pear.test.com/pear/foo
http://test.com/apple/foo -> http://apple.test.com/apple
http://apple.test.com/pear/foo -> http://pear.test.com/pear/foo

First off I can’t get the simplest check to work

redir 301 {
   if {hostonly} not_starts_with pear
   pear/ {scheme}://pear.test.com{uri}
}

Seems to have no effect. I guess I don’t really understand how it redir works

Under apache in the /pear folder I have this in a .htaccess

RewriteCond %{HTTP_HOST}      !^pear\.test\.com$ [NC]
RewriteRule ^(.*)$           https://pear.test.com/pear/$1 [L,R=permanent]

Basically if you made it to the pear folder but you’re not coming from the pear domain then redirect to the pear domain. I have a similar .htaccess.

Is that possible in caddy? I see redir says only exact matches work so maybe that’s why I’m stuck? I saw something somewhere else about paths that don’t start with / matching everything that starts with the path but maybe redir doesn’t support that?

Caddy’s config works on the basis of labels. Any directives you put under a label apply for that label.

So for any site you want to be redirecting away from if they try to request /pear, you need to add your redirect.

It would look like this:

test.com {
  ...
  redir /pear https://pear.test.com:<port>/pear{uri}
}

apple.test.com {
  ...
  redir /pear https://pear.test.com:<port>/pear{uri}
}

#etc...

Alternately if your sites are all under one label:

test.com, apple.test.com {
  ...
  redir {
    if {host} not_starts_with pear
    /pear https://pear.test.com:<port>/pear{uri}
  }
}

So I tried this

http://test.com:8080 {
  root public_html
  redir pear http://pears.test.com:8080{uri} 301
  redir apple http://apple.test.com:8080{uri} 301
}

http://apple.test.com:8080 {
  root public_html
  redir pear http://pears.test.com:8080{uri} 301
}

http://pears.test.com:8080 {
  root public_html
  redir apple http://apple.test.com:8080{uri} 301
}

and this

http://test.com:8080 {
  root public_html
  redir pear/ http://pears.test.com:8080{uri} 301
  redir apple/ http://apple.test.com:8080{uri} 301
}

http://apple.test.com:8080 {
  root public_html
  redir pear/ http://pears.test.com:8080{uri} 301
}

http://pears.test.com:8080 {
  root public_html
  redir apple/ http://apple.test.com:8080{uri} 301
}

and this

http://test.com:8080 {
  root public_html
  redir /pear/ http://pears.test.com:8080{uri} 301
  redir /apple/ http://apple.test.com:8080{uri} 301
}

http://apple.test.com:8080 {
  root public_html
  redir /pear/ http://pears.test.com:8080{uri} 301
}

http://pears.test.com:8080 {
  root public_html
  redir /apple/ http://apple.test.com:8080{uri} 301
}

and this

http://test.com:8080 {
  root public_html
  redir /pear http://pears.test.com:8080{uri} 301
  redir /apple http://apple.test.com:8080{uri} 301
}

http://apple.test.com:8080 {
  root public_html
  redir /pear http://pears.test.com:8080{uri} 301
}

http://pears.test.com:8080 {
  root public_html
  redir /apple http://apple.test.com:8080{uri} 301
}

And none of them really work. To make it clear the different between the 4 are

  /path
  /path/
   path
   path/

None of them redirect all paths, they only redirect the exact path which means only the first 2 work but for 2 absolute urls.

Is there anyway to redirect for all paths under a folder?

That indicates you’re probably missing a closing brace, or your Caddyfile is malformed. I’m guessing test.com is actually a site address. What’s your full Caddyfile?

Sorry, was in the process of updating my response after deleting it but discourse has no way to edit and undelete in one action. Instead undelete being a separate action means the message I was trying to hide is visible while I’m fixing it :roll_eyes:

from is always an exact match, except when it is / (a catch-all).

The path will always start with a leading slash, so you can eliminate any version that doesn’t have one.

That leaves you with /pear and /pear/, which are both valid, and a client may request either. Your options for handling both are:

redir /pear [desination]
redir /pear/ [destination]

or:

redir {
  if {path} starts_with /pear
  / [destination]
}

The latter option becomes more attractive if you have a large section of possible locations that can be grouped under one basepath, but with only two (trailing slash vs. no trailing slash), they’re equally verbose, and while I personally lean towards the former as a matter of readability / principle, it’s entirely up to you.

Thank you for the example, I’m getting closer to understanding.

Still back to my initial goal which is anything under /pear goes to pear.test.com and anything under /apple goes to apple.test.com I tried this

http://pear.test.com:8080 {
  redir {
    if {path} starts_with /apple/
    / http://apple.test.com:8080{uri} 301
  }
}

http://apple.test.com:8080 {
  redir {
    if {path} starts_with /pear/
    / http://pear.test.com:8080{uri} 301
  }
}

http://test.com:8080 {
  redir {
    if {path} starts_with /apple/
    / http://apple.test.com:8080{uri} 301
  }
  redir {
    if {path} starts_with /pear/
    / http://pear.test.com:8080{uri} 301
  }
}

The first 2 sections work but the last one gets

 Caddyfile:22 - Parse error: rule with duplicate 'from' value: / -> http://apple.test.com:8080{uri}

Is there a way to handle that case?

Oh, that.

There is a way to handle that, but it’s not pretty. I understand the devs are working on a solution for this particular check.

It involves a lot of rewrites, which won’t run into the same check.

http://pear.test.com:8080 {
  rewrite {
    if {path} starts_with /apple
    to /redir-pear-to-apple/
  }
  redir /redir-pear-to-apple/ http://apple.test.com:8080{uri}
}

http://apple.test.com:8080 {
  rewrite {
    if {path} starts_with /pear
    to /redir-apple-to-pear/
  }
  redir /redir-apple-to-pear/ http://pear.test.com:8080{uri}
}

http://test.com:8080 {
  rewrite {
    if {path} starts_with /apple
    to /redir-test-to-apple/
  }
  redir /redir-test-to-apple/ http://apple.test.com:8080{uri}

  rewrite {
    if {path} starts_with /pear
    to /redir-test-to-pear/
  }
  redir /redir-test-to-pear/ http://pear.test.com:8080{uri}
}

P.S: Don’t check starts_with /apple/, it will not catch requests for /apple - checking starts_with /apple will catch both.

1 Like

(Yes, I’m pushing for a change by the next release that will disable the duplication check for redir rules containing an if statement. That should resolve the issue. And I have encountered this problem too so it’s bugging me as well.)

3 Likes

Thank you! Works great!

1 Like

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