Nginx auth_basic 目录密码保护实战:给后台和敏感目录加一道门

为什么个人站长也需要目录级的访问控制

很多个人站长把全站安全都押在登录页和后台密码上,却忽略了一个事实:网站里总有一些目录本身就不该让任何人随便访问。比如后台的 /admin、临时放数据的 /data、只给自己用的 /backup、开发时留下的 /phpmyadmin,甚至是还没上线的新版本目录。这些路径一旦被爬虫扫到,就等于给攻击者递上了一张地图。

有人会说,加个 robots.txt 禁止收录不就行了?完全不行。robots.txt 只是「请求爬虫不要抓」,它管不了恶意扫描器,也管不了拿浏览器直接敲地址的人。真正能挡住访问的,是服务器层面的认证——在请求到达 PHP 之前就把它拦下来。Nginx 自带的 ngx_http_auth_basic_module 就是干这件事的,它由 Nginx 官方维护、无需装任何第三方模块,是每个站长都该掌握的基础技能。

auth_basic 的原理:一次请求的完整流程

HTTP 基本认证的机制非常朴素。浏览器第一次访问受保护目录时,Nginx 发现该位置配了 auth_basic,于是返回一个 401 Unauthorized 响应,并带上 WWW-Authenticate: Basic realm="..." 这个响应头。浏览器看到这个头,就弹出原生的账号密码输入框。用户填完之后,浏览器把 用户名:密码 用 Base64 编码,放进请求头 Authorization: Basic xxx 再发一次。Nginx 解码后与密码文件里的哈希比对,通过就放行,不通过就继续返回 401。

这里必须讲清一个常见误解:Base64 不是加密,它是可逆编码。抓包工具能立刻把 Basic 后面的内容还原成明文。所以基本认证必须配合 HTTPS 使用,否则密码在网络里相当于裸奔。好在现在 Let's Encrypt 免费证书唾手可得,全站 HTTPS 已是标配,这一点不难满足。

另外,密码文件里存的也不是明文。Nginx 用 crypt() 兼容的哈希,即使文件被读取,攻击者也不能直接拿去登录。当然,前提是你用的是足够强的密码。

第一步:生成密码文件

Nginx 不自带生成工具,密码文件需要用系统里的 htpasswd 或者 openssl 来创建。Debian/Ubuntu 上先装一下 apache2-utils:

apt-get install -y apache2-utils

然后创建密码文件并添加第一个用户。-c 表示新建文件,只有第一次添加用户时才加 -c,之后再加用户千万不要带,否则会把之前的所有用户全部清空:

# 新建文件并添加用户 zhangsan(会提示输入两次密码)
htpasswd -c /etc/nginx/.htpasswd zhangsan

# 追加第二个用户,注意没有 -c
htpasswd /etc/nginx/.htpasswd lisi

# 查看文件内容(只能看到用户名和哈希)
cat /etc/nginx/.htpasswd

生成的文件内容形如 zhangsan:$apr1$abcd1234$XXXXXXXXXXXXXXXXXXXXXX,冒号左边是用户名,右边是哈希。接下来务必收紧权限,这个文件绝不能让 web 进程之外的用户读取(其实 Nginx 的 worker 进程能以 root 启动后降权读取,但文件权限保持 640 就够,属主设为 root):

chown root:root /etc/nginx/.htpasswd
chmod 640 /etc/nginx/.htpasswd

如果你实在不想装 apache2-utils,也可以用 openssl 手工生成,效果等价:

printf "zhangsan:$(openssl passwd -apr1 '你的密码')\n" > /etc/nginx/.htpasswd

第二步:在 Nginx 中启用基本认证

最直接的用法是给某个 location 加上两行配置:

location /admin/ {
    auth_basic "Restricted Area";
    auth_basic_user_file /etc/nginx/.htpasswd;
}

auth_basic 后面的字符串就是浏览器弹窗里显示的提示语(realm)。它不参与认证判断,但写清楚能让你自己一眼知道是哪个站点在要密码,比如写 "zz1984 后台"

对于 Typecho、WordPress 这类程序,后台路径通常是固定的。如果你用的是 Typecho,后台入口是 /admin/;WordPress 则是 /wp-admin//wp-login.php。给这两个位置加认证,能挡掉绝大部分自动化的后台爆破脚本——它们连登录页都看不到,自然无从撞库。

改完配置一定要先测试语法再重载,别直接 restart 把自己关在门外:

nginx -t && systemctl reload nginx

第三步:更精细的访问控制组合

基本认证常和 IP 白名单配合使用。思路是:固定 IP(比如你自己的办公网络)直接放行,其他来源则要求输入密码。这样自己访问不被打扰,外人仍然进不来:

location /admin/ {
    satisfy any;
    allow 203.0.113.10;      # 你的固定 IP
    allow 10.0.0.0/8;        # 内网段
    deny all;
    auth_basic "Admin Area";
    auth_basic_user_file /etc/nginx/.htpasswd;
}

这里的关键是 satisfy any。它的含义是「满足任意一条访问规则即可放行」:要么 IP 在白名单里,要么通过了密码认证,命中一条就够。如果把 any 换成 all,那就变成「IP 必须在白名单并且密码也正确」才能访问,用于对安全要求极高的场景。

需要特别注意 allow/deny 的判定顺序:Nginx 从上往下匹配,命中第一条规则就生效。所以 deny all 必须放在最后,否则后面的 allow 永远轮不到。这是新手最容易写反的地方。

常见坑:认证成了摆设,或者把自己关在门外

配置看似简单,实际部署时却经常踩坑。下面几个是反复出现的:

坑一:location 匹配优先级搞错。Nginx 的 location 匹配有明确优先级:= 精确匹配最优先,其次是 ^~ 前缀匹配,再是带正则的 ~~*,最后才是普通前缀匹配。如果你写了一个正则 location 去处理 PHP 请求,同时又写了个普通前缀 location /admin/ 做认证,那么 /admin/index.php 这类请求可能被正则那个 location 先接走,认证配置根本没生效——后台照样能打开。判断方法很简单:用 curl -I https://你的域名/admin/ 看返回码。如果返回 200 而不是 401,说明认证压根没生效,去检查 location 优先级。

坑二:把认证加在了静态资源上,页面样式全丢。有时候有人图省事,直接在 server 块顶层写 auth_basic,结果全站包括 CSS、JS、图片都要认证。虽然浏览器通常会自动带上凭据不会明显报错,但 CDN、外部调用、搜索引擎抓取全废了。正确做法永远是把 auth_basic 写在最小范围的 location 里。

坑三:忘记给 PHP 单独放开。某些配置下认证目录里的 PHP 由 fastcgi 段处理,出现「HTML 要密码、PHP 不用密码」的诡异现象。这时要确保认证配置和 fastcgi_pass 在同一个 location 内,或者确认它们被同一层 location 覆盖。

坑四:把自己也锁了。最常见的悲剧是:在 location / 上加了认证,然后重载 Nginx,结果连网站首页都要密码,而密码忘了。救急办法是直接从服务器上删掉 auth_basic 那两行并 reload。所以养成习惯——改认证配置前,先在一个测试 location 上验证,确认密码能登录,再推广到正式目录

基本认证的局限与更好的替代

讲完怎么用,也要讲清它不适合什么。基本认证有几个先天不足:第一,只有一组账号密码,无法区分不同用户的权限;第二,没有会话管理,浏览器记住凭据后,退出只能靠关浏览器,没有真正的「登出」;第三,密码在客户端缓存,共享电脑上有泄露风险;第四,认证提示是浏览器原生弹窗,样式无法定制。

所以基本认证的定位很明确:它是「守门」工具,不是「用户系统」。用它保护后台入口、临时目录、内部工具、测试环境,非常合适;但如果要做多用户、细粒度权限的正式业务系统,就该用程序自带的用户体系(比如 Typecho 的登录机制),或者更完整的方案如 mTLS 客户端证书、OAuth2 等。

对于绝大多数个人站长,一个务实的组合是:主站走正常程序登录,后台入口再套一层 auth_basic 双保险,同时用 allow/deny 限制来源 IP。这套组合成本极低、效果显著,能把绝大多数自动化攻击挡在门外。安全这件事从来不是一步到位,而是把每一层能做的都做到位,让攻击者的成本高于收益——对个人站来说,这已经足够。

小结

Nginx 的 auth_basic 是一个被严重低估的功能:零依赖、开销极低、配置就两行,却能把最容易被扫描的后台和敏感目录保护起来。核心要点记住四条:密码文件用 htpasswd 生成且只有第一次带 -c;认证必须配合 HTTPS;allow/deny 顺序不能写反、deny all 放最后;改配置前一定 nginx -t 并想好退路。把这四点做对,再配合 IP 白名单和 satisfy 的灵活组合,你就能用最小的成本,给自己的服务器加一道真正的门。

Last modification:September 14th, 2026 at 12:31 pm

Leave a Comment