文章标题:nginx解决文件夹访问指向问题的探索历程
事情的起因是项目中有一个文件域名,dns将其解析到了Linux系统的服务器上,有个nginx服务对挂载在nas上的一个文件目录做了反向代理。现在有新需求,要让客户依旧通过这个域名作为访问入口,去访问同样在nas盘里且和那个目录同级的另一个目录下的文件。
比如说,通过nginx反向代理访问的nas盘下的目录路径是/mnt/picture/
,访问的url是:https://picture.test.cn/cache/20260101/1749690185966.jpg
而和picture
同级的目录路径是:/mnt/bak/
,期望的访问url是:https://picture.test.cn/bak/20251024/1749690185966.jpg
我最先想到的办法是借助软连接,把/mnt/bak
目录软连接到/mnt/picture/bak
目录,命令如下:
ln -s /mnt/bak/ /mnt/picture/bak
nginx的简略配置如下:
server {
listen 443;
server_name picture.test.cn;
...
location / {
root /mnt/picture/;
}
}
访问是没问题的,当然也可以通过增加一个location,用bak
关键字来代理/mnt/bak/
目录。
本地开发机器是Windows10,在一个盘符下创建了两个文件夹来模拟Linux机器上的挂载盘目录进行开发测试,nginx的简略配置如下:
server {
listen 8066;
server_name picture.test.cn;
location / {
root D:/tmp/mnt/picture/;
}
}
两个目录分别是:D:/tmp/mnt/picture/
、D:/tmp/mnt/bak
我在D:/tmp/mnt/picture/
下创建了一个D:/tmp/mnt/bak
目录的快捷方式,快捷方式名称叫bak
,还在hosts文件中配置了域名到本机的映射。
但访问http://picture.test.cn:8066/bak/20251024/1749690185966.jpg
时却提示文件不存在,nginx的error日志如下:
2026/10/24 09:41:16 [error] 16584#7532: *4 CreateFile() "D:/tmp/mnt/picture/bak/20251024/1749690185966.jpg" failed (3: The system cannot find the path specified), client: 127.0.0.1, server: picture.test.cn, request: "GET /bak/20251024/1749690185966.jpg HTTP/1.1", host: "picture.test.cn:8066"
明明在文件系统里点击快捷方式能进到对应的文件夹,可nginx却识别不了,问了下ai(不得不说现在ai真是个好帮手),核心原因有这些:
1.快捷方式的本质是特殊文件
Windows快捷方式(.lnk)属于系统级的符号链接文件,得由操作系统来解析路径。但Nginx作为Web服务器,只会读取实际的文件或目录内容,不会去解析.lnk文件的目标路径。
2.Nginx配置依赖物理路径
Nginx的root或alias指令得指向真实的目录(比如D:\folder),要是指向包含快捷方式的目录,Nginx会把快捷方式当作普通文件而非目录入口,从而导致访问失败。
3.路径安全限制
Nginx默认禁止访问路径中包含符号链接的文件(这是安全策略),而且Windows路径格式(反斜杠\、中文名等)容易引发兼容性问题。
ai给出的解决方案是:
1.用NTFS联结点实现类似Linux软链接的效果
文件系统得是NTFS,以管理员身份运行CMD,命令如下:
mklink /J "D:\tmp\mnt\picture\bak" "D:\tmp\mnt\bak"
2.修改Nginx配置来启用路径解析
在nginx的location下添加如下配置:
disable_symlinks off; # 允许解析符号链接
其中disable_symlinks在nginx的1.1.15版本中添加,默认是开启的,需要在编译nginx时加上--with-file-aio
参数。
通过nginx -v
和nginx -V
确认项目上使用的nginx版本是1.22.0,服务器上的nginx也启用了--with-file-aio
参数。
为啥Nginx在Linux支持软链接而Windows不支持快捷方式呢?ai给出了解答:
- Linux:Nginx通过系统调用(比如open())访问文件时,内核会自动解析软链接路径,服务不需要额外处理。
- Windows:Nginx直接读取文件系统,但.lnk是需要解析的二进制文件,不是标准的目录入口,所以没法识别它指向的真实路径。
按照ai给出的解决方案,我在本地Windows机器上用mklink创建了目录联结:
mklink /J "D:\tmp\mnt\picture\bak" "D:\tmp\mnt\bak"
再次访问http://picture.test.cn:8066/bak/20251024/1749690185966.jpg
就能访问了。
我让ai详细说说mklink
mklink是Windows系统(Vista及以上版本)提供的命令行工具,用来创建文件或目录的链接,功能类似Linux中的ln命令,但实现机制和类型划分更复杂。以下是它的核心特性和使用场景的详细解析:
1.mklink的链接类型
类型 | 参数 | 特点 | 适用场景 |
---|---|---|---|
符号链接 | /D | 跨分区支持,需要管理员权限,路径可以是相对或绝对,远程访问时可能不解析 | 需灵活路径映射(如开发环境配置) |
目录联结 | /J | 仅限目录,不需要管理员权限,远程访问时自动解析目标路径 | 迁移大目录保留原始路径(如程序文件) |
硬链接 | /H | 仅限文件,跨分区无效,删除任一链接不影响其他副本 | 节省空间(如备份重复文件) |
2.关键差异与注意事项
-
权限要求
符号链接(/D)和文件硬链接(/H)需要管理员权限运行CMD,目录联结(/J)则不需要。 -
路径解析
符号链接支持相对路径(比如..\target),联结只支持绝对路径。
移动符号链接可能会让相对路径失效,而联结的绝对路径仍然有效。 -
跨分区限制
硬链接不能跨分区,符号链接和联结可以跨分区但需要目标存在。
可以看到通过快捷方式和mklink命令创建出来的文件可以同名,但类型不一样,一个是快捷方式,一个是文件夹:
ai还提到nginx的disable_symlinks
标签是为了防止攻击者通过上传符号链接文件访问/etc/passwd
等系统文件,我在Windows上测试添加这个标签时发现,Windows上的nginx不支持这个标签,核心限制原因如下:
1.平台兼容性问题
disable_symlinks
是Nginx针对类Unix系统(比如Linux)设计的特性,依赖文件系统符号链接权限检查机制,而Windows NTFS文件系统的符号链接管理逻辑和它不兼容,导致该指令在Windows环境下无效或无法正确解析。
尝试在Windows配置中使用时,nginx -t会提示语法错误或未知指令(比如unknown directive “disable_symlinks”)。
2.版本无实质支持
就算用户用的是较新的Nginx 1.22.0或更高版本,其Windows编译版也没适配这个功能。官方在Windows构建中没启用相关依赖模块(比如–with-file-aio)。
本地Windows机器只是用来开发测试,不影响,生产服务器上得严格做好目录权限的划分,并且在上传文件的入口强烈校验文件的类型。