给站点加一条图片目录映射,想把/upload/下的请求指到另一块大盘上。配置写完reload,站点首页正常,所有图片404。文件明明在,用ls能看到,权限也对。
打开error.log,找到这么一行:open() “/data/uploadsimages/a.jpg” failed (2: No such file or directory)。路径少了一个斜杠。真实目录是/data/upload/images,拼接结果变成了/data/uploadsimages。文件确实在盘上,目录权限也没问题,是nginx把路径拼错了,跟磁盘无关。
root和alias的算法不一样。root是把完整URI直接接在后面:root /data;加上请求/upload/a.jpg,取的是/data/upload/a.jpg,location写什么不影响结果。alias是把location匹配到的那一段换掉:location /upload/配alias /data/images/,请求/upload/a.jpg取的是/data/images/a.jpg,前缀被替换,后缀原样保留。出错就出在这条替换规则上,alias的值末尾有没有斜杠、location末尾有没有斜杠,四个组合里只有一种是对的。写成location /upload/ { alias /data/images; },替换后前缀是/data/images,剩下a.jpg直接接上去,成了/data/imagesa.jpg。写成location /upload { alias /data/images/; },匹配到的/upload被换掉,开头那个斜杠也一起没了,拼出来是/data/imagesa.jpg。两种都少一个斜杠。
正确写法是两边都带斜杠,或者两边都不带,保持一致:location /upload/ { alias /data/images/; }。这样/upload/整段被替换成/data/images/,后面的a.jpg接上去正好。记忆办法是把alias当成一个前缀替换器,它替换的是location里那段字符串,两段的边界必须对齐。
还有一种情况是根本不该用alias。单站点、目录就在站点根下面,用root最省心。比如站点根是/www/wwwroot/example.com,图片在根下的images目录,直接root /www/wwwroot/example.com;就行,不需要额外写location。只有当目录跑到了站点根之外,比如挂载了另一块数据盘、或者多个站点共享一个静态资源目录,alias才派得上用场。
alias还有两条限制要知道。第一,不能写在正则location里,写location ~ ^/upload/ { alias ...; }会直接报错,nginx启动就失败,提示“alias” directive is not allowed here。要用正则就得改用root配rewrite,或者把捕获组写进alias。第二,alias配try_files在部分版本上有问题,早年的nginx会把拼接结果算错,PHP框架里常见的try_files $uri $uri/ /index.php?$query_string在alias下可能绕不出来。遇到这种就别混用,改用root加一条rewrite。
排查这类问题有个快办法:把error.log的级别临时调到debug,或者直接在配置里用return 200把拼出来的路径打回响应体,access_log里加$request_filename也能看到nginx最终解析出的完整路径。看路径跟实际差在哪,一眼就知道是哪一段没对齐。
这条比对着文档猜快得多,尤其是目录层级深、前缀有重叠的时候。比如同时有/upload/和/upload/temp/两条规则,prefix匹配走最长的那条,谁生效不看配置顺序,看长度。这两种毛病经常一起犯,斜杠没对齐加上前缀被更长的规则抢走,日志里看到的路径会跟你想的完全不一样,得两个都查一遍才收得住。
改完reload,再刷一次页面。所有图片回来,日志里不再有open() failed。以后配新的目录映射,先想清楚是替换还是拼接,再决定用哪个指令。
A5创业网 版权所有