Can't get some simple redirects to work

We’ve built a new site for a client and now it’s live, we need to redirect a bunch of old URLs to a their new equivalents while maintaining the end of the path, no matter how deep it goes from this point. Seems like it should be simple enough. The caddy file currently looks like this…

http://999.999.999.999 {
    redir https://example.com{uri}
}

https://example.com/some_old_path/{path} {
    redir https://example.com/a-new-path/{path}
}

https://example.com {
    proxy / 127.0.0.1:8000 {
        transparent
    }

    log /var/log/caddy/access.log
    errors /var/log/caddy/error.log

    gzip
    expires
    tls me@example.com
}

The main site is proxied to a Django app. The redirect from the IP address to the domain works. The redirect below it doesn’t.

I’ve also tried doing them inside the main domain directive, like this…

http://999.999.999.999 {
    redir https://example.com{uri}
}

https://example.com {
     
    redir https://example.com/some_old_path/{path} /a-new-path/{path} 301
    
    proxy / 127.0.0.1:8000 {
        transparent
    }

    log /var/log/caddy/access.log
    errors /var/log/caddy/error.log

    gzip
    expires
    tls me@example.com
}

And many, many variations on that theme, using full paths, with and without domains on both sides of the directive and so on. Everything I’ve tried, the request still gets proxied to the Django app, which then serves a 404. Am I doing Caddy wrong?

Hi @drcongo, welcome to the Caddy Community.

The label here:

https://example.com/some_old_path/{path} {
    redir https://example.com/a-new-path/{path}
}

Was probably a cause of some issue. Placeholders can’t be used in a site label in this way. A placeholder is not a matcher or a substitution, either - placeholders are set in the context of a request and are available to middlewares that are designed to use them.

As such, it’s difficult, but doable, to redirect in this manner. The redir directive has no means to manipulate the URI by itself dynamically, but it can make use of existing placeholders.

rewrite takes place before redir, can manipulate the URI with substitutions, and can provide a {rewrite_uri} placeholder. We’ll need to make use of it like so (the following example is untested):

https://example.com {
  rewrite /some_old_path {
    r ^/some_old_path(.*)/?$
    to /a-new-path{1}
  }
  redir {
    # check if we just did a rewrite
    if {rewrite_uri} starts_with /a-new-path
    # redirect client to location we rewrote to dynamically
    / {rewrite_uri} 301
    # alternately specify host:
    # / https://example.com{rewrite_uri} 301
  }
  # additional site config
  ...
}

Also, this format:

redir https://example.com/some_old_path/{path} /a-new-path/{path} 301

Won’t work, because the [from] is matching the URI, not the entire URL. Strip the host/scheme from that and it would function, but not how you think (refer: {path} is not a matcher or substitution, above).

https://caddyserver.com/docs/redir
https://caddyserver.com/docs/rewrite
https://caddyserver.com/docs/placeholders

1 Like

Crikey, that seems a lot more complicated than I’d expect from Caddy, especially as I’ll have a lot of these. Thanks though, I’ll give it a go.

1 Like

OK, I’ve created a bare vagrant box with the following structure…

serve/
├───index.html
├───newpath/
|   ├────index.html
|   ├────test.html
|   └────subfolder/
|        ├────test.html
|        └────index.html
├───oldpath/
    ├────index.html
    ├────test.html
    └────subfolder/
         ├────test.html
         └────index.html

In this example, I want to redirect any requests for anything along oldpath/**/* to the newpath equivalent. So these redirects need to happen:

  • caddy.dev/oldpath/ redirects to caddy.dev/newpath/
  • caddy.dev/oldpath/test.html redirects to caddy.dev/newpath/test.html
  • caddy.dev/oldpath/subfolder/ redirects to caddy.dev/newpath/subfolder/
  • caddy.dev/oldpath/subfolder/test.html redirects to caddy.dev/newpath/subfolder/test.html

I’ve set up a super basic caddy file like this…

    caddy.dev:80 {

        rewrite /oldpath {
            r ^/oldpath(.*)/?$
            to /newpath{1}
        }
        redir {
            if {rewrite_uri} starts_with /newpath
            / {rewrite_uri} 301
        }

        root /vagrant/serve/

        log /var/log/caddy/access.log
        errors /var/log/caddy/error.log

        tls off
    }

What actually happens for the desired redirects…

  • caddy.dev/oldpath/ still serves caddy.dev/oldpath/ with a 200
  • caddy.dev/oldpath/test.html still serves caddy.dev/oldpath/test.html with a 200
  • caddy.dev/oldpath/subfolder/ still serves caddy.dev/oldpath/subfolder/ with a 200
  • caddy.dev/oldpath/subfolder/test.html still serves caddy.dev/oldpath/subfolder/test.html with a 200

Additional info:

Caddy 0.10.7 on Ubuntu 17.04 server

I won’t be in a position to assist much further for the next few days, but on a hunch, try my example but without the basepath on the rewrite (i.e. rewrite { instead of rewrite /oldpath {). Everything else looks correctly configured, that’s the only thing I don’t usually do (I added it to this example to try and avoid unnecessary regex checks, the basepath is a pre-regex substring check).

Thanks for the help @Whitestrake

With this Caddyfile, I get a too many redirects error.

    caddy.dev:80 {

        rewrite {
            r ^/oldpath(.*)/?$
            to /newpath{1}
        }
        redir {
            if {rewrite_uri} starts_with /newpath
            / {rewrite_uri} 301
        }

        root /vagrant/serve/

        log /var/log/caddy/access.log
        errors /var/log/caddy/error.log

        tls off
    }

Does it do that in an incognito / private browing session?

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