如何解决Caddy的重定向(redir)和重写(rewrite)的冲突 作者: 灯小笼 时间: 2018-08-31 分类: 工具 网站改版后,很难确保新的网址和旧的网址保持一致,因此总需要写一些重定向规则,确保原来被搜索引擎抓取的页面能重定向到新的URL。对于一个基于typeocho的php类型的网站,重写也总是必要的。在使用nginx服务器的时候,重写和重定向是在一起的,并没有什么歧义产生,但是,到caddy里边,这两个概念是区分开的。更为不好的地方在于,两者并存的时候,会出现意想不到的结果。 比如,最早本站发现有一些老页面的流量过来,但是会进到404页面。 ``` 5 203.208.60.50 - - [26/Aug/2018:20:53:44 +0800] "GET /article/223672015323021312 HTTP/1.1" 404 2057 "-" "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)" ``` 按照官方说明,在Caddyfile中使用__[redir](https://dengxiaolong.com/caddy/cn-doc/http.redir.html "redir")__命令予以重定向。 ```caddy redir 301 { /article/223672015323021312 /article/2017/06/php-git-http-server.html /article/2017/06/5.html /article/2017/06/php-git-http-server.html } rewrite { to {path} {path}/ / } ``` 重启caddy,但是发现并没有生效,还是和之前效果一样。 检查配置文件,发现应该是因为里边存在__[rewrite](https://dengxiaolong.com/caddy/cn-doc/http.rewrite.html "rewrite")__指令,而caddy并没有处理两者之间的优先级导致的。[bing](https://www.bing.com/ "bing")搜索一下,发现果然有这方面的讨论。 其中,[hlidotbe][2]提到: > Looking a bit further, I see why it happens. in caddyhttp/httpserver/plugin.go "redir" is after "header", way after "rewrite". 主要说的是`redir`是在在`rewrite`后面执行的,也就是说,先执行了rewrite逻辑,然后再执行redir逻辑。 查看[源代码](https://github.com/mholt/caddy/blob/master/caddyhttp/httpserver/plugin.go),果然如此: ```go // directives is the list of all directives known to exist for the // http server type, including non-standard (3rd-party) directives. // The ordering of this list is important. var directives = []string{ // primitive actions that set up the fundamental vitals of each config "root", "index", "bind", "limits", "timeouts", "tls", // services/utilities, or other directives that don't necessarily inject handlers "startup", // TODO: Deprecate this directive "shutdown", // TODO: Deprecate this directive "on", "supervisor", // github.com/lucaslorentz/caddy-supervisor "request_id", "realip", // github.com/captncraig/caddy-realip "git", // github.com/abiosoft/caddy-git // directives that add listener middleware to the stack "proxyprotocol", // github.com/mastercactapus/caddy-proxyprotocol // directives that add middleware to the stack "locale", // github.com/simia-tech/caddy-locale "log", "cache", // github.com/nicolasazrak/caddy-cache "rewrite", "ext", "minify", // github.com/hacdias/caddy-minify "gzip", "header", "geoip", // github.com/kodnaplakal/caddy-geoip "errors", "authz", // github.com/casbin/caddy-authz "filter", // github.com/echocat/caddy-filter "ipfilter", // github.com/pyed/ipfilter "ratelimit", // github.com/xuqingfeng/caddy-rate-limit "expires", // github.com/epicagency/caddy-expires "forwardproxy", // github.com/caddyserver/forwardproxy "basicauth", "redir", "status", ``` [hlidotbe](https://github.com/hlidotbe) 还提到 > I think there's really only two (and a half) options: > 1 merge redir and rewrite as most/all web servers do which makes the problem disappear and remove all ambiguity about it; > 2 order rewrite after redir which may break some use cases but should limit surprises for most; > 2.5 allow rewrite to redirect too but that would render redir redundant and often unused when rewrites are involved; 简单翻译一下: 我认为最后解决方式只有2.5个选项: 1 像大多数web服务器软件一样(如nginx),合并`redir`和`rewrite`两个指令,让分歧彻底消失; 2 使`rewrite`指令在`redir`指定后面执行,这虽然会影响部分使用场景(有时候可能想重写后还能重定向),但会解决绝大部分疑惑; 2.5 允许`rewrite`指令支持重定向,不过这样就会让`redir`显得多余了。 既然,问题出现了,而且也没有解决方案,我们只能自己绕路而行了。 思路就是: * rewrite命令想办法让需要redir的路径不重写; * 其他一切不变 最后,来看一下改进的Caddyfile文件: ```caddy # 先让rewrite过滤掉需要redir的路径 rewrite { if {path} not /article/223672015323021312 if {path} not /article/2017/06/5.html to {path} {path}/ / } # 先执行rewrite,再执行redir redir 301 { /article/223672015323021312 /article/2017/06/php-git-http-server.html /article/2017/06/5.html /article/2017/06/php-git-http-server.html } ``` 经过这样调整,caddy终于能正常相应redir的请求了。虽然这样来看,比nginx的`rewrite`配置要复杂不少,但其他地方的配置,尤其是https的配置,实在是要高明得多。对于普通的blog来说,caddy还是显得要便捷得多。 标签: caddy