引言:Nginx 不只是反向代理
很多运维工程师对 Nginx 的认知停留在"反向代理"和"负载均衡",但实际上 Nginx 在安全防护方面也相当强大——限流可以防止 CC 攻击和 API 滥用,黑白名单可以精准控制访问来源,基础安全配置可以防护常见的 Web 攻击。
这篇文章系统讲解 Nginx 的限流机制、黑白名单配置、以及生产环境推荐的安全配置,帮助你把 Nginx 打造成一道坚实的安全防线。
前置知识:Nginx 基础配置、HTTP 协议基础
实验环境:CentOS Stream 9 / Ubuntu 24.04 LTS,Nginx 1.26+
1 限流机制深度解析
1.1 限流的类型
Nginx 限流类型: │ ├── 请求速率限制 (rate limiting) │ └── limit_req:限制每秒/每分钟请求数 │ ├── 并发连接限制 (connection limiting) │ └── limit_conn:限制单个 IP 的并发连接数 │ ├── 带宽限制 (bandwidth limiting) │ └── limit_rate:限制单连接传输速率 │ └── 白名单/黑名单 (access limiting) └── geo + map:基于 IP 的访问控制
1.2 限流算法原理
令牌桶算法(Token Bucket):
算法描述:
- 桶中最多存放 N 个令牌
- 每秒放入 R 个令牌
- 每个请求消耗 1 个令牌
- 桶满时,令牌溢出
示意图:
令牌生成速率 R=10/秒
桶容量 B=20
时间 t=0: [桶满了]
┌─────┐
│●●●●●│
│●●●●●●●●●●●│ 20个
│●●●●●●●●●●●●●●●│
└─────┘
时间 t=1: [消耗了10个,放入10个]
┌─────┐
│●●●●●│
│●●●●●●●│ 20个
│●●●●●●●●●│
└─────┘
漏桶算法(Leaky Bucket):
算法描述:
- 请求以任意速率进入桶
- 桶以固定速率漏水
- 桶满时,新请求被拒绝
示意图:
请求输入 (可变速率) 桶 (队列) 输出 (固定速率)
●●●● ┌─────┐ ●
●●●●●●●● ──────> │ ● │ ──────> ●●●
│ ●● │ ●●●
│ ●●●│ ●●●●
└─────┘
(桶满则拒绝)
Nginxlimit_req使用令牌桶算法,limit_conn使用简单计数。
1.3 limit_req 配置详解
# /etc/nginx/nginx.conf # 定义限流区域(共享内存) # 语法:limit_req_zone key zone=name:size rate=rate; # - key: 用于识别请求的变量(通常是 $binary_remote_addr) # - zone: 区域名称和共享内存大小 # - rate: 速率(r/s 或 r/m) limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s; limit_req_zone $binary_remote_addr zone=login_limit:10m rate=1r/m; limit_req_zone $binary_remote_addr zone=search_limit:10m rate=5r/s; # 按服务器维度限流 limit_req_zone $server_name zone=server_limit:10m rate=1000r/s; http { # 限流区域 limit_req_zone $binary_remote_addr zone=global:100m rate=100r/s; server { listen 80; server_name example.com; # 全局限流 limit_req zone=global burst=20 nodelay; # API 限流:每秒 10 请求,允许突发 50 location /api/ { limit_req zone=api_limit burst=50 nodelay; proxy_pass http://backend; } # 登录限流:每分钟 1 次,防止暴力破解 location /login { limit_req zone=login_limit burst=5 nodelay; limit_req_status 429; # 超限返回 429 proxy_pass http://backend; } # 搜索限流:每秒 5 请求 location /search { limit_req zone=search_limit burst=20 nodelay; proxy_pass http://backend; } } }
1.4 突发与排队机制
# burst 参数详解
# burst=N: 允许 N 个请求排队
# nodelay: 不延迟排队请求,立即处理
# 示例 1:允许突发,不延迟
limit_req_zone $binary_remote_addr zone=test:10m rate=10r/s;
location / {
limit_req zone=test burst=20 nodelay;
}
# 效果:允许最多 30 个请求同时处理(10 个正常 + 20 个突发)
# 示例 2:允许突发,延迟处理
location / {
limit_req zone=test burst=20; # 超过 10r/s 的请求会延迟
}
# 效果:请求被延迟而不是拒绝,响应变慢但不返回错误
# 示例 3:不允许突发
location / {
limit_req zone=test burst=0; # 严格按速率处理
}
# 效果:超过速率的请求直接拒绝
# 示例 4:delay 参数(延迟突发)
location / {
limit_req zone=test burst=20 delay=10;
}
# 效果:前 10 个突发请求正常处理,后续请求延迟
1.5 limit_req 日志控制
# 限流日志配置
limit_req_log_level info warn error;
server {
listen 80;
server_name example.com;
# 设置返回状态码
limit_req_status 503;
location /api/ {
limit_req zone=api_limit burst=50 nodelay;
# 触发限流时记录 warn 级别日志
proxy_pass http://backend;
}
}
2 限并发(limit_conn)配置
2.1 并发连接限制原理
# 定义并发限制区域
# 语法:limit_conn_zone key zone=name:size;
limit_conn_zone $binary_remote_addr zone=addr:10m;
limit_conn_zone $server_name zone=server_conn:10m;
server {
listen 80;
server_name example.com;
# 限制单个 IP 最多 10 个并发连接
limit_conn addr 10;
# 限制整个服务器最多 1000 个并发连接
limit_conn server_conn 1000;
# 设置返回状态码
limit_conn_status 503;
location / {
proxy_pass http://backend;
}
}
2.2 针对特定资源的并发限制
# 下载站点并发限制
server {
listen 80;
server_name download.example.com;
# 整体并发限制
limit_conn addr 100;
location /download/ {
# 下载目录限制为 5 个并发
limit_conn addr 5;
limit_conn_status 503;
alias /var/www/download/;
autoindex on;
}
location /video/ {
# 视频限制为 2 个并发(节省带宽)
limit_conn addr 2;
limit_conn_status 503;
alias /var/www/video/;
}
}
2.3 带宽限制
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://backend;
}
# 限制下载速度(字节/秒)
# 这里 500k = 512000 bytes/s
location /download/ {
alias /var/www/download/;
limit_rate_after 10m; # 前 10MB 不限速
limit_rate 500k; # 之后限速 500KB/s
}
# 根据状态限制速度
map $status $limit_rate {
200 1m; # 成功响应限速 1MB/s
302 5m; # 重定向限速 5MB/s
default 500k; # 其他情况限速 500KB/s
}
location /video/ {
alias /var/www/video/;
limit_rate $limit_rate;
}
}
3 黑白名单配置
3.1 基于 IP 的访问控制
server {
listen 80;
server_name example.com;
# 基本deny/allow 语法(旧方法,按顺序匹配)
# 注意:旧方法按顺序匹配,匹配到就停止
# 允许内网,deny 其他所有
allow 10.0.0.0/8;
allow 172.16.0.0/12;
allow 192.168.0.0/16;
deny all;
# 或者反过来:拒绝某些IP
deny 192.168.1.100;
deny 192.168.1.200;
allow all;
location / {
proxy_pass http://backend;
}
}
3.2 geo 模块实现动态黑白名单
# geo 模块:根据 IP 地址映射值
geo $geo {
default 0;
# 白名单
10.0.0.0/8 1;
172.16.0.0/12 1;
192.168.0.0/16 1;
# 黑名单(高风险 IP)
192.168.1.100 2;
192.168.1.200 2;
# 特定 IP
1.2.3.4 0; # 明确放行
5.6.7.8 99; # 明确拒绝
}
server {
listen 80;
server_name example.com;
# 黑名单直接拒绝
if ($geo = 99) {
return 403;
}
# 白名单特殊处理
if ($geo = 1) {
# 内网用户不限流
set $limit_rate 0;
}
location / {
# 黑名单用户限制
if ($geo = 2) {
limit_rate 10k;
}
proxy_pass http://backend;
}
}
3.3 map 模块实现复杂规则
# map 模块:根据变量值映射到另一个值
# 定义 IP 名单文件
map $remote_addr $ip_whitelist {
default 0;
include /etc/nginx/whitelist.conf;
}
map $remote_addr $ip_blacklist {
default 0;
include /etc/nginx/blacklist.conf;
}
map $request_uri $rate_limit {
default 100r/s;
~* ^/api/v1/login 1r/m;
~* ^/api/v1/search 5r/s;
~* ^/admin 10r/s;
}
server {
listen 80;
server_name example.com;
# 黑名单拒绝
if ($ip_blacklist = 1) {
return 403;
}
# 限流
limit_req_zone $binary_remote_addr zone=api:10m rate=$rate_limit;
location / {
limit_req zone=api burst=50 nodelay;
proxy_pass http://backend;
}
}
3.4 维护模式实现
# 维护模式配置
map $ Maintenance_mode $limit {
default 0;
on 99; # 维护模式返回 99,即无限流
}
geo $whitelist {
default 0;
# 允许运维人员 IP 访问
10.0.1.0/24 1;
192.168.1.100 1;
}
server {
listen 80;
server_name example.com;
# 维护开关(可通过 API 或文件控制)
set $maintenance false;
# 或者从文件读取
# set_from_config_file $maintenance /var/www/maintenance.flag;
# 白名单在维护模式下仍可访问
if ($whitelist = 1) {
set $maintenance false;
}
if ($maintenance = true) {
return 503;
}
location / {
proxy_pass http://backend;
}
# 503 错误时显示维护页面
error_page 503 @maintenance;
location @maintenance {
root /var/www/html;
rewrite ^(.*)$ /maintenance.html break;
internal;
}
}
4 黑白名单文件管理
4.1 名单文件格式
# /etc/nginx/whitelist.conf 格式 # 每行一个 IP 或网段 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16 203.0.113.0/24 123.45.67.89 # /etc/nginx/blacklist.conf 格式 # 被禁止的 IP 192.168.1.100 10.10.10.50 123.123.123.123 # 也可以是网段 192.168.100.0/24
4.2 动态更新名单脚本
#!/bin/bash
# update_blacklist.sh - 自动更新黑名单
BLACKLIST_FILE="/etc/nginx/blacklist.conf"
BLACKLIST_URL="https://api.example.com/blacklist/ipv4"
# 备份当前黑名单
cp"$BLACKLIST_FILE""${BLACKLIST_FILE}.bak"
# 下载新黑名单
curl -s"$BLACKLIST_URL">"$BLACKLIST_FILE"
# 检查文件是否有效
if[ $? -eq 0 ] && [ -s"$BLACKLIST_FILE"];then
# 测试 Nginx 配置
nginx -t
if[ $? -eq 0 ];then
# 重新加载 Nginx
nginx -s reload
echo"$(date): 黑名单已更新"
else
# 恢复备份
mv"${BLACKLIST_FILE}.bak""$BLACKLIST_FILE"
echo"$(date): Nginx 配置测试失败,已恢复"
fi
else
echo"$(date): 下载失败,已恢复"
mv"${BLACKLIST_FILE}.bak""$BLACKLIST_FILE"
fi
# 添加到 crontab echo"*/5 * * * * /usr/local/bin/update_blacklist.sh">> /var/spool/cron/root # 或者使用 systemd timer cat > /etc/systemd/system/nginx-blacklist.service << 'EOF' [Unit] Description=Nginx Blacklist Update After=network.target [Service] Type=oneshot ExecStart=/usr/local/bin/update_blacklist.sh EOF cat > /etc/systemd/system/nginx-blacklist.timer << 'EOF' [Unit] Description=Nginx Blacklist Update Timer Requires=nginx-blacklist.service [Timer] OnCalendar=*/5 * * * * Persistent=true [Install] WantedBy=timers.target EOF sudo systemctl enable --now nginx-blacklist.timer
5 基础安全配置
5.1 隐藏版本号和指纹
server {
listen 80;
server_name example.com;
# 隐藏 Nginx 版本号
server_tokens off;
# 隐藏 PHP 版本号(需在 php-fpm 配置)
# /etc/php-fpm.d/www.conf
# php_admin_value[expose_php] = off
# 自定义响应头
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline';" always;
# 移除暴露信息的响应头
proxy_set_header X-Powered-By "";
proxy_set_header X-AspNetMvc-Version "";
location / {
proxy_pass http://backend;
}
}
5.2 防止常见攻击
# 防止 SQL 注入
if ($query_string ~* "union.*select.*(") {
return 403;
}
# 防止 XSS
if ($query_string ~* "
Nginx的限流机制深度解析