前言
之前官网重构的时候,为了保证原页面的 seo 权重,就有针对一些旧页面路由进行 301 重定向到新路由。 具体看 nginx 通过 301 跳转将旧页面的 seo 权重转移到新页面上, 但是后面又提了一个新需求, 就是针对一些路由进行目录结构上的调整。
比如原先是 a.html
后面会移动到 product/a.html
, 而且 html 的名称还不能变。 所以这时候从用户访问来看就是:1
2
3访问 www.foo.com/a.html --> 301 重定向 --> www.foo.com/product/a.html
考虑到大部分是有小语种路径的,比如 zh-cn,那么就是
访问 www.foo.com/zh-cn/a.html --> 301 重定向 --> www.foo.com/zh-cn/product/a.html
原先的方法不适用
如果是原先这种方式的话:1
2
3if ($request_uri ~* "a.html") {
rewrite ^/(.*)a.html$ /$1product/a.html permanent;
}
就会发现其实重定向后的 url 也是符合这个规则的,就会导致 nginx 无限重定向。 所以这样子是不行的
尝试一: 使用正则表达式排除特定字符串
其实 正则表达式是可以 零宽度断言(?!exp),来进行排除某个字符串的。
这个是因为正则有一个向前查找的语法(也叫顺序环视) (?=exp)
, (?=exp)
会查找 exp 之前的【位置】, 如果将等号换成感叹号,就变成了否定语义,也就是说查找的位置的后面不能是 exp。
一般情况下 ?!
要与特定的锚点相结合,例如^
行开头或者$
行结尾。
而本例的这个 product
, 它并不会在路由的开头 (可能还有多语言路径), 也不会在结尾 (后面还有具体的 html 路由), 原则上可以用:1
^(?!.*product)/a.html$
来实现, 不过我试了一下,还是不行,会漏掉有匹配 a.html
的情况。但是如果只是单独的排除掉 product/a.html
的路由。那么其实用这个语法是可以的:1
^(?!.*product/a\.html).*$
这样子只要路由不包含 product/a.html
字串的,就会匹配到。 但是还不满足我的需求, 我的需求是要在当前符合 a.html
的匹配下,还要排除掉 product/a.html
的情况, 但是很显然, 按照我的测试来说,这个得分为两个匹配来处理。
但是如果要分为两个匹配的话,我就不需要用这种复杂的正则来处理了,直接在 nginx 那边启用变量就行了。
启用变量来区分
如果要启用变量来处理,那么就很简单:
- 初始化一个变量 0
- 匹配到
a.html
, 变量设置为 1 - 匹配到
product/a.html
, 变量设置为 0, 表示不跳转 - 最后判断变量如果为 1 的话,那么就重定向
具体逻辑如下:1
2
3
4
5
6
7
8
9
10
11
12set $needrw "0";
if ($request_uri ~* "/a.html") {
set $needrw "1";
}
if ($request_uri ~* "/product/a.html") {
set $needrw "0";
}
if ($needrw = "1") {
rewrite ^/(.*)a.html /$1product/a.html permanent;
}
这样子就简单易懂了。 当前缺点就是 多写了好多代码。
那么如果有多个路由都要这么干呢,那不是直接吐血了。比如:1
2
3a -> kk/a.html
b -> age/b.html
c --> jj/c.html
那么就要用 更有效的方式,将 $needrw
当做要跳转的变量, 然后直接判断不匹配的方式,就行了:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29set $needrw "0";
# 先匹配固定的,然后设置对应的跳转路由
if ($request_uri ~* "/b.html") {
set $needrw "age/b.html";
}
if ($request_uri ~* "/a.html") {
set $needrw "kk/a.html";
}
if ($request_uri ~* "/c.html") {
set $needrw "jj/c.html";
}
# 排除掉特定的不跳转的路由
if ($request_uri ~* "/age/b.html") {
set $needrw "0";
}
if ($request_uri ~* "/kk/a.html") {
set $needrw "0";
}
if ($request_uri ~* "/jj/c.html") {
set $needrw "0";
}
# 最后如果是要跳转的,直接取变量来跳转
if ($needrw != "0") {
rewrite ^(/?)(.*)/(.*).html $1$2/$needrw permanent;
}
这样子虽然代码也不少, 但是比一个一个指定会好很多。而且变量只需要一个就行了。
参考资料: