我想实现一个 “根据不同的 header 中的 Accept-Language
返回不同的文件” 功能,我写的配置文件如下:
map $http_accept_language $locale {
default "en-US";
~*en "en-US";
~*zh "zh-CN";
}
server {
listen 80;
server_name _;
location / {
rewrite_log on;
rewrite ^/(.*)$ /prerendered/$locale/$1;
}
location /prerendered/en-US {
root /usr/share/nginx/html;
# try_files $uri $uri/ $uri.html /prerendered/en-US/index.html =404;
}
location /prerendered/zh-CN {
root /usr/share/nginx/html;
# try_files $uri $uri/ $uri.html /prerendered/zh-CN/index.html =404;
}
}
大体思路就是利用 map 定义一个 $locale
变量,然后 rewrite 到对应目录。
文件结构:
/usr/share/nginx/html/prerendered
├── en-US content:
│ ├── a.html a en-US
│ └── index.html index en-US
└── zh-CN
├── a.html a zh-CN
└── index.html index zh-CN
curl 命令和结果:
$ curl http://127.0.0.1/a.html
a en-US
$ curl http://127.0.0.1/a.html -H 'Accept-Language: en'
index en-US
$ curl http://127.0.0.1/a.html -H 'Accept-Language:zh'
index zh-CN
从第二个命令开始,路径就被错误地重定向到了 /prerendered/en-US/
,后面的 a.html
消失了。
nginx 对第二个命令的日志:
2020/09/09 09:45:16 [notice] 29#29: *4 "^/(.*)$" matches "/a.html", client: 172.17.0.1, server: _, request: "GET /a.html HTTP/1.1", host: "127.0.0.1"
2020/09/09 09:45:16 [notice] 29#29: *4 rewritten data: "/prerendered/en-US/", args: "", client: 172.17.0.1, server: _, request: "GET /a.html HTTP/1.1", host: "127.0.0.1"
可以看到,这个 rewrite 规则是命中了的,但是替换的时候后面的 $1
却没有替换上;而且只有 Accept-Language
包含 zh/en
才会出现,如果是 空 或者es
(默认 map 到 en ),a.html
还是正常替换的。
求助各位这是哪里出现的问题?
1
superrichman 2020-09-09 18:29:19 +08:00 via iPhone
location 又没有定义$1 的值,后面那节就是空的,重定向没毛病
|
2
gwy15 OP @superrichman 谢谢回答,但是好像不是这样的 XD
在 stackoverflow 上有大佬解释了下,大概是; rewrite 部分匹配到的组,在计算 replacement 时由于懒计算会再进行 map 部分的匹配,如果正则匹配成功 $1 就会被重置,因此 $1 会变为空串。这里可以用命名组解决: rewrite ^/(?<myuri>.*)$ /prerendered/$locale/$myuri last; |
3
ysc3839 2020-09-09 18:44:39 +08:00
直接用 try_files 是没问题:
``` location / { try_files /prerendered/$locale$uri =404; } ``` 对于“匹配任意 URL 然后进行 rewrite”的情况,使用 try_files 的话性能更好。rewrite 应该在需要匹配复杂规则时才使用。 |
4
gwy15 OP @ysc3839 这里是我简化过的可重现 demo,实际上 rewrite 是在一个 if 块里面,匹配到爬虫 UA 才会 rewrite 。谢谢你。
|
5
ysc3839 2020-09-09 18:52:41 +08:00
再者,就算要用 rewrite 实现“匹配任意 URL”,直接用
rewrite ^ /prerendered/$locale$uri; 即可 因为你本来就可以通过 $uri 这个变量访问到 URL,完全没必要再匹配一次。 “^” 是匹配开头,单独一个“^”也就等价于“匹配任意字符串”。 |
6
superrichman 2020-09-09 18:53:11 +08:00 via iPhone
@gwy15 是我理解错了,学到新东西了谢谢
|