只开一个洞:在 IP 白名单的 Nginx 站点里开放静态子目录
做过一次小型部署之后我发现,真正麻烦的不是"把页面放上去",而是"既要对外开放,又不能把整站的门打开"。
我的场景很典型:一台云服务器上跑着个人测试站点,nginx 里用 allow/deny 把全站锁成只允许自己家的公网 IP 访问;现在需要额外放一个静态作品页(一个 2048 小游戏),要求任何人在公网上都能打开。直觉做法是把 deny all 去掉,但那等于把整站暴露出来。正确的做法是只为一个路径开洞。
一、先看清 nginx 的匹配顺序
nginx 的 location 匹配优先级是:= 精确 > ^~ 前缀(命中后不再匹配正则)> 正则 ~/~* > 普通前缀 > /。
关键点是 ^~:一旦命中这个前缀,就不会再去匹配正则表达式。这一点在"站点里有一堆安全正则(比如禁止访问 .git、.env、*.bak)"的环境里很重要——用 ^~ 能让你的路径不受那些正则影响,行为可预期。
二、最小改动:加一个 location
假设站点根是 /www/wwwroot/site,要把 /hc-activity/ 对外开放:
location ^~ /hc-activity/ {
alias /www/wwwroot/hc-activity/;
allow all;
index index.html;
limit_except GET { deny all; }
}
四个细节值得说清楚:
alias而不是root。root会拼成/根路径/hc-activity/index.html,alias则是直接替换前缀,变成/www/wwwroot/hc-activity/index.html。目录和 URI 不同名时必须用alias。allow all能覆盖 server 级的deny all。nginx 的 access 模块是"就近替换":location 里只要出现了任何 allow/deny 指令,就整套替换掉从 server 继承的那一组,而不是追加。所以不需要先把 server 的deny all删掉。limit_except GET { deny all; }把方法限制成只读,顺手挡掉 PUT/DELETE 这类将来可能被加进来的方法。- 如果站点还有 HTTP→HTTPS 的跳转逻辑(
location /里try_files再return 301),这个新 location 必须写在它前面,否则永远跳不到。
三、改之前先备份,改完先测语法
cp -a site.conf site.conf.bak.$(date +%Y%m%d-%H%M%S)
nginx -t && nginx -s reload
nginx -t 一定要跑。alias 少写一个斜杠、location 写错括号,都会让 reload 之后整站 404——而 -t 能提前拦住。
四、一个很坑的验证方式
改完在服务器上执行:
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1/hc-activity/index.html
如果返回 404,先别急着改配置——很可能是因为 Host 头不匹配。nginx 按 server_name 选虚拟主机,用 127.0.0.1 访问时 Host 是 127.0.0.1,匹配不到 server_name 你的IP 的那个 server 块,于是落到了默认站点,当然 404。
带上 Host 再测就对了:
curl -s -o /dev/null -w '%{http_code}\n' -H 'Host: 你的IP' http://127.0.0.1/hc-activity/index.html
五、最终验收要看两件事
- 新路径能开:从一台不在白名单里的机器(比如手机流量)访问,应该 200,且内容正确;
- 原有防护没破:访问站点根路径,应该仍然是被拒的状态。
两条都通过,才算真的"只开了一个洞"。
六、小结
deny all 加一个 ^~ 前缀的 location,是这种"整站受限 + 局部公开"需求里改动最小的方案:不用动证书、不用动 443、不用动原有业务配置,出问题回滚一个文件就行。真正的风险不在写法,而在验证方式——用 127.0.0.1 测出 404 就以为配置错了,是最容易浪费时间的地方。
- 点赞
- 收藏
- 关注作者
评论(0)