Newbie issue -- converting 301 redirects from nginx to caddy


(Sudarsh) #1

Hi,

I have the following four redir rules in Nginx. I tried the documentation, but I’m having trouble figuring out the correct version for caddy. Please note that I don’t want to use rewrite, as I believe it will increase the load on the server. I also need to explicitly issue a 301 for SEO purposes. Therefore I am looking to use redir.

#redirecting everything to /n/ folder to root folder

    location ~ ^/n/(.*) {return 301 /$1;}

#replacing '_ ’ or ‘-’ at a particular point in the url with ‘/’

    location ~ ^/(p-d|g-g)(-|/|_)([0-9]+)-(.*) {return 301 /$1/$3/$4;}

#redirecting date-based wordpress url

    location ~ ^/([0-9]+)/([0-9]+)/([0-9]+)/(.*) {return 301 /a/1/$4;}

#redirecting non-existent authors/numerical urls

    location ~ ^/(author/r-o-s|9) { return 410;}

I also had a ‘try’ directive for WP Super Cache, which I have managed to convert into an equivalent caddy command

Super Cache try command –

location / {
try_files /wp-content/cache/supercache/ultra.news/$request_uri/index-https.html $uri $uri/ /index.php?$args;
}

Caddy equivalent –

rewrite / {
if {path} not_match ^/wp-admin
to /wp-content/cache/supercache/{host}{uri}index-https.html {path} {path}/ /index.php?{query}
}


(Matthew Fay) #2

This isn’t possible without rewriting, because you need to be able to perform regex substitutions. redir on its own isn’t capable of the kind of dynamic redirection you need.

I don’t think there’s any reason to believe that rewriting would increase the load on the server any more than using regex in nginx would, though. You can use substring if checks to avoid unnecessary regex usage as well.

# Redirect /n/some/path to /some/path
rewrite /n/ {
  r ^/n(.*)
  to {1}
}
redir {
  if {path} starts_with /n/
  / {rewrite_uri}
}
Test Caddyfile
localhost:2015
browse
rewrite {
  if {path} starts_with /n/
  r ^/n(.*)
  to {1}
}
redir {
  if {path} starts_with /n/
  / {rewrite_uri}
}
log / stdout "{common} /// {rewrite_uri}"
Test output
whitestrake at apollo in ~
→ curl -i localhost:2015/test
HTTP/1.1 404 Not Found
Content-Type: text/plain; charset=utf-8
Server: Caddy
X-Content-Type-Options: nosniff
Date: Mon, 06 Nov 2017 23:52:32 GMT
Content-Length: 14

404 Not Found

whitestrake at apollo in ~
→ curl -i localhost:2015/n/test
HTTP/1.1 301 Moved Permanently
Location: /test
Server: Caddy
Date: Mon, 06 Nov 2017 23:52:35 GMT
Content-Length: 40
Content-Type: text/html; charset=utf-8

<a href="/test">Moved Permanently</a>.
Test log
::1 - - [07/Nov/2017:09:52:32 +1000] "GET /test HTTP/1.1" 404 14 /// /test
::1 - - [07/Nov/2017:09:52:35 +1000] "GET /n/test HTTP/1.1" 301 40 /// /test

(Sudarsh) #3

Ok. That sounds like a possibility. Will check and let you know.

At least in NGINX documentation, they clearly say that using ‘rewrite’ instead of ‘return’ would be less efficient.

Of course, given the use of regex, there is already a performance hit involved.

But these rules comes after the cache directive, and 99.9% of the traffic is handled by the cache directive which comes before this.

Hopefully, the order of execution of these directives will be sequential, right? As in, if the cache is a miss, it ‘falls down’ to the next directive, and then on to the next one and so on, and finally onto fastcgi handler?

In other words, if the cache is a miss, it won’t directly go to the fastcgi part, given how I’ve structured the rewrite rule (below)?

if {path} not_match ^/wp-admin
to /wp-content/cache/supercache/{host}{uri}index-https.html {path} {path}/ /index.php?{query}
} ```

(Matthew Fay) #4

Not sure how nginx perform their rewrites. In Caddy, it’s difficult to understate the complexity; unless I’m mistaken, it’s about as simple as a variable reassignment in some cases.

The Caddyfile is parsed at startup into rules, and the ordering of directives is not preserved. Instead, for each request, Caddy steps through each directive in order: https://github.com/mholt/caddy/blob/5f39cbef9480eb748a58d8b3b0d3f479b7671ebd/caddyhttp/httpserver/plugin.go#L447-L522

Rewrite happens as just one (relatively early) stage of many; when called, rewrite picks one of the matching rewrite rules, applies it, and then hands off to the next directive[1]. If multiple rules apply, it picks the first rule with the longest base path[2].

As for the rewrite you’ve written, it’s all evaluated at once during this rewrite stage - quite simply, it goes through the list of targets one by one, stopping when it finds a real file (or directory), or at the last option[3].

One implication of this is that, in my example, I should have specified a base path for my rewrite to ensure its precedence over yours (i.e. rewrite /n/ { ... }, instead of using an if).


[1] https://github.com/mholt/caddy/blob/5f39cbef9480eb748a58d8b3b0d3f479b7671ebd/caddyhttp/rewrite/rewrite.go#L48-L55
[2] https://github.com/mholt/caddy/blob/5f39cbef9480eb748a58d8b3b0d3f479b7671ebd/caddyhttp/httpserver/middleware.go#L95-L107
[3] https://github.com/mholt/caddy/blob/5f39cbef9480eb748a58d8b3b0d3f479b7671ebd/caddyhttp/rewrite/to.go#L32-L53


(Sudarsh) #5

This is very useful info. In fact, gives me a whole new perspective about how to structure the rules.

I will now be paying more attention to the basepath. Wouldn’t have earlier.


(system) #6

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