Nginx 限制 IP 访问用 allow 和 deny
Nginx 限制 IP 访问:allow 与 deny 指令完全指南
在 Web 服务器管理中,控制谁能访问你的网站、管理后台或敏感接口至关重要。Nginx 提供了两个轻量级、高性能的模块指令 allow 和 deny 来实现基于客户端 IP 地址的访问控制,无需引入额外的防火墙或复杂的验证层。本教程将从零开始,帮助你理解并熟练运用这两个指令。
理解核心概念:访问控制顺序与继承
allow 和 deny 指令由标准 HTTP 模块 ngx_http_access_module 提供,该模块默认编译在内,无需额外安装。它们的工作方式类似白名单和黑名单,但执行逻辑严格遵循从上到下的顺序匹配。
allow address | CIDR | unix: | all;:允许指定 IP 或网络范围访问。deny address | CIDR | unix: | all;:禁止指定 IP 或网络范围访问。
当 Nginx 处理一个请求时,会按配置文件中编写的顺序依次检查 allow 和 deny 规则。一旦客户端的 IP 匹配到某一条规则,则立即执行对应的操作(放行或拒绝),并停止后续检查。因此,规则的书写顺序会直接影响最终效果。
基础用法:保护敏感目录
最常见的场景是限制整个网站、特定路径或管理后台只能由特定的 IP 地址访问。
假设你的办公网络出口 IP 为 203.0.113.5,你希望 /admin 目录下的所有资源仅允许从该 IP 访问,其余全部拒绝。
location /admin {
allow 203.0.113.5;
deny all;
}
解析:
- 第一条
allow 203.0.113.5;明确放行该 IP。 - 第二条
deny all;捕获所有未被前面规则匹配的请求(即其他所有 IP)并直接返回 403 Forbidden。
如果你希望允许一个内部网络段(例如 10.0.0.0/24)而拒绝特定某个捣乱的 IP(例如 10.0.0.66),可以这样写:
location /api {
deny 10.0.0.66;
allow 10.0.0.0/24;
deny all;
}
逻辑顺序:先精确拒绝 10.0.0.66,然后放行同一网段内的其他机器,最后拒绝网段外的所有请求。如果将 deny 10.0.0.66 放在最后,它就不会生效,因为该 IP 会先被 allow 10.0.0.0/24 匹配并放行。
指令的作用域:http, server 与 location
allow 和 deny 可以编写在 http、server 和 location 块中,其作用域和继承关系如下:
1. http 块(全局控制)
在 http 块内定义的规则会应用于所有虚拟主机(server 块)和所有路径,除非被下层覆盖。
http {
# 全局默认拒绝所有访问,然后按需在下面放行
deny all;
server {
listen 80;
server_name example.com;
# 因为上方 http 块已全局拒绝,此处需显式放行
allow 203.0.113.0/24;
allow 198.51.100.1;
deny all;
...
}
}
2. server 块(站点级控制)
应用于特定虚拟主机。如果某个 server 块内没有写任何 allow/deny,则会继承上级(http 块)的规则。
server {
listen 80;
server_name intranet.example.com;
allow 10.0.0.0/8;
deny all;
...
}
3. location 块(路径级控制)
最精细的控制层。location 块会继承 server 块的规则,但你可以在 location 内部重新定义规则来覆盖或增强。
重要特性:当 location 中定义了新的 allow/deny 规则时,父级定义的规则在该 location 内会被完全忽略,只有当前 location 中的规则生效。
示例:整体站点对公网开放,但 /private 仅限内网。
server {
listen 80;
server_name public-site.com;
# 整个站点默认允许所有访问
allow all;
location /private {
# 该 location 内重新定义规则,父级的 allow all 不再起作用
allow 192.168.1.0/24;
deny all;
}
}
支持的网络地址格式
除了常见的单 IP 和 CIDR 格式,还支持以下特殊值:
all:匹配所有地址。- IPv4 单地址:
192.168.1.1 - IPv4 CIDR:
10.0.0.0/16或10.0.0.0/255.255.0.0(两种掩码写法均支持) - IPv6 单地址:
2001:0db8::1 - IPv6 CIDR:
2001:0db8::/32 - Unix 域套接字:
unix:(极少使用)
处理被拒绝的请求:自定义错误页
当请求被 deny 拦截时,Nginx 默认返回 HTTP 状态码 403 Forbidden,并附带一个简单的默认错误页面。你可能希望返回一个更友好的页面或一个 JSON 提示。
可以在 server 或 location 上下文中使用 error_page 指令自定义 403 页面:
server {
...
# 禁止访问的返回自定义 HTML
error_page 403 /403.html;
location = /403.html {
root /usr/share/nginx/html;
internal;
}
location /secure {
allow 203.0.113.5;
deny all;
}
}
对于 API 接口,也可以直接返回 JSON:
location /api/internal {
allow 10.0.0.0/24;
deny all;
# 自定义 403 返回 JSON
error_page 403 = @denied;
location @denied {
default_type application/json;
return 403 '{"error":"access denied","code":403}';
}
}
进阶技巧与常见陷阱
1. 使用地图(map)动态控制
如果需要在多个 server 或 location 中复用同一套复杂的 IP 规则,建议将 allow/deny 与 map 和 geo 模块结合,但简单场景下直接书写即可。
2. 与 satisfy 指令的相互作用
当配置了多种访问控制(例如 IP 限制 + HTTP 基本认证)时,satisfy 指令会决定它们的逻辑关系(all 或 any)。默认 satisfy all; 表示所有条件必须同时满足。如果你设置了 IP 白名单,同时又配置了 auth_basic,那么即使 IP 在白名单内,也会要求输入密码。此时可设置 satisfy any;,意指 IP 满足 或者 密码满足即放行。
location /staging {
allow 127.0.0.1;
allow 203.0.113.0/24;
deny all;
auth_basic "Staging Area";
auth_basic_user_file /etc/nginx/.htpasswd;
satisfy any; # IP 在白名单内 或 密码正确即可访问
}
3. 实时生效与测试
修改完配置后,务必先执行语法检查,再重载 Nginx,避免因笔误导致服务中断。
sudo nginx -t
sudo nginx -s reload
测试时,可以从受限制 IP 外访问,预期返回 403;从允许 IP 访问,预期正常加载。
4. 注意 CDN 或反向代理环境
如果你的 Nginx 位于负载均衡器、CDN 或反向代理之后,直接获取到的客户端 IP 会是代理的 IP,而不是用户的真实 IP。此时 allow/deny 将作用于代理 IP,导致规则失效。
必须确保通过 ngx_http_realip_module 正确设置了真实 IP,例如:
set_real_ip_from 10.0.0.0/8;
real_ip_header X-Forwarded-For;
real_ip_recursive on;
然后在 allow/deny 规则中使用经过还原的真实 IP。
完整配置示例
下面是一个典型的组合场景:一个公司内部使用的 GitLab 服务,要求办公网络可以无限制访问,同时允许两个外部顾问的固定 IP 访问,其余全部拒绝,并返回统一的 JSON 错误。
server {
listen 443 ssl;
server_name git.internal.net;
# 真实 IP 还原(如果前方有其他代理)
set_real_ip_from 172.16.0.0/12;
real_ip_header X-Forwarded-For;
real_ip_recursive on;
# 定义 403 错误返回 JSON
error_page 403 = @blocked;
location @blocked {
default_type application/json;
return 403 '{"code":403,"message":"IP not allowed"}';
}
location / {
# 办公网络完整网段
allow 203.0.113.0/24;
# 外部顾问固定 IP
allow 198.51.100.77;
allow 203.0.114.20;
# 拒绝其余所有
deny all;
proxy_pass http://gitlab_backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
总结
allow和deny是 Nginx 内置的轻量级 IP 访问控制手段。- 规则按顺序匹配,命中即停止,顺序至关重要。
- 可在
http、server、location不同层级生效,下层可覆盖上层。 - 配合
error_page可自定义拒绝时的响应。 - 代理环境中务必结合
real_ip模块使用,确保基于真实 IP 过滤。
掌握这两个指令后,你就能轻松实现地域限制、后台保护、内网服务隔离等常见需求,且性能开销极低。