最近部署 MediaWiki 时遇到一个需求,即用 NGINX 实现类似 Overlay FS 的特性。
具体地,由于我使用了 Git 版本的 MediaWiki,而又需要在目录中添加一些自定义的文件(如 logo、favicon.ico
、各种 verification 文件等)。为了不污染 Git,并在升级时减少麻烦,因此我想把这些文件独立于源码放置。
抽象一下问题,NGINX 原来需要从 /srv/foo/orig
提供服务,现在需要它除了搜索原来的路径,还要额外从 /srv/foo/overlay
尝试搜索文件(注意与 Overlay FS 不同的是,优先使用原来的路径)。并且由于 MediaWiki 依赖 PHP,因此还需要能够同时处理动态与静态文件。
经过我和坏人的一些讨论,以及数次摸索,获得了以下的配置:
root /srv/foo/orig;
location @overlay {
root /srv/foo/overlay;
try_files $uri $uri/ @orig;
}
location @orig {
rewrite ^/([^?]*)(?:\?(.*))? /index.php?title=$1&$2 last;
}
location / {
index index.php;
try_files $uri $uri/ @overlay;
}
在这些配置下,NGINX 在遇到形如 /baz
的路径时,会顺次进行如下尝试:
- 匹配
/
:尝试/srv/foo/orig/baz
和/srv/foo/orig/baz/
; - 转向
@overlay
:尝试/srv/foo/overlay/baz
和/srv/foo/orig/baz/
; - 转向
@orig
:重写为/index.php
的 query string,并由 FastCGI 处理请求。此部分适用于在两个位置都没有匹配到静态文件的情况,可以进行任何兜底操作,比如交由动态程序处理、重定向(rewrite
),或者返回错误(error_page
)。注意此部分不能合并到@overlay
中,因为两者的root
不同。
上面的 @overlay
和 @orig
是 NGINX 的 named location,官方文档描述如下:
The “@” prefix defines a named location. Such a location is not used for a regular request processing, but instead used for request redirection. They cannot be nested, and cannot contain nested locations.
使用 named location 的一大优点是,进行跳转不影响实际的 URL,避免了各种复杂的匹配处理。注意 @overlay
中必须使用 root
切换目录,因为 named location 中不能使用 alias
。此外,由于某些我还没有搞清的机制(与 NGINX 的 context 有关),配置中必须级联 try_files
来进行链式查找,而不能写成 try_files $uri $uri/ @overlay @orig
。
如果只有静态文件,那么只需要保留 @overlay
即可。如果想要优先搜索 overlay 目录(与 Overlay FS 的用法相同),则需要另一种写法:
root /srv/foo/orig;
location @overlay {
root /srv/foo/overlay;
try_files $uri $uri/ @orig;
}
location @orig {
try_files $uri $uri/ @orig_dyn;
}
location @orig_dyn {
rewrite ^/([^?]*)(?:\?(.*))? /index.php?title=$1&$2 last;
}
location / {
index index.php;
try_files @overlay;
}
由于 root
相同,这里的 @orig_dyn
是可以直接合并到 @orig
中的。