Nginx的限流机制深度解析

描述

引言: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)

 

算法描述:
    - 请求以任意速率进入桶
    - 桶以固定速率漏水
    - 桶满时,新请求被拒绝

示意图:

    请求输入 (可变速率)     桶 (队列)        输出 (固定速率)

    ●●●●                    ┌─────┐          ●
    ●●●●●●●●    ──────>     │  ●  │  ──────> ●●●
                            │  ●● │           ●●●
                            │  ●●●│           ●●●●
                            └─────┘
                            (桶满则拒绝)

 

Nginx limit_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 ~* "

 

5.3 HTTPS 安全配置

 

server {
    listen 443 ssl http2;
    server_name example.com;

    # SSL 证书
    ssl_certificate /etc/nginx/ssl/example.com.crt;
    ssl_certificate_key /etc/nginx/ssl/example.com.key;

    # TLS 版本控制(禁用 SSLv3 和 TLS 1.0/1.1)
    ssl_protocols TLSv1.2 TLSv1.3;

    # 密码套件配置
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
    ssl_prefer_server_ciphers off;

    # OCSP Stapling
    ssl_stapling on;
    ssl_stapling_verify on;
    resolver 8.8.8.8 8.8.4.4 valid=300s;
    resolver_timeout 5s;

    # SSL 会话缓存
    ssl_session_cache shared10m;
    ssl_session_timeout 1d;
    ssl_session_tickets off;

    # HSTS (HTTP Strict Transport Security)
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;

    # 安全头部
    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;

    # SSL 证书安全检查(定期执行)
    # openssl s_client -connect example.com:443 -tls1_2
}

# HTTP 重定向到 HTTPS
server {
    listen 80;
    server_name example.com;
    return 301 https://$server_name$request_uri;
}

 

5.4 请求大小和超时控制

 

http {
    # 请求体大小限制(默认 1M)
    client_max_body_size 10m;
    client_body_buffer_size 128k;

    # 请求头大小限制
    large_client_header_buffers 4 8k;

    # 超时设置
    client_body_timeout 15s;
    client_header_timeout 15s;
    send_timeout 30s;

    # 保持连接
    keepalive_timeout 65;
    keepalive_requests 100;

    server {
        listen 80;
        server_name example.com;

        # 上传目录特殊限制
        location /upload {
            client_max_body_size 100m;
            client_body_timeout 300s;
        }
    }
}

 

6 CC 攻击防护实战

6.1 CC 攻击识别

 

#!/bin/bash
# detect_cc.sh - 检测 CC 攻击

LOG_FILE="/var/log/nginx/access.log"
THRESHOLD=100  # 单 IP 每分钟请求数阈值

echo"=== CC 攻击检测 ==="

# 统计最近 1 分钟每个 IP 的请求数
awk -v limit=$THRESHOLD'{
    ip[$1]++
    time[$1, $4]++
}
END {
    for (key in time) {
        split(key, parts, SUBSEP)
        if (time[key] > limit) {
            print parts[1], "请求数:", time[key]
        }
    }
}'"$LOG_FILE" | sort | uniq

echo""
echo"=== 短时间高频请求 TOP 20 ==="
awk '{print $1}'"$LOG_FILE" | sort | uniq -c | sort -rn | head -20

echo""
echo"=== 响应码分布 ==="
awk '{print $9}'"$LOG_FILE" | sort | uniq -c | sort -rn

 

6.2 CC 攻击防护配置

 

# CC 防护配置示例

# 1. 基础限流
limit_req_zone $binary_remote_addr zone=cc_protect:100m rate=100r/s;

# 2. 按 URI 限流(某些 URI 更容易被攻击)
limit_req_zone $binary_remote_addr zone=api_limit:50m rate=30r/s;
limit_req_zone $binary_remote_addr zone=search_limit:50m rate=5r/s;

# 3. 浏览器指纹验证
map $http_user_agent $is_bot {
    default                         0;
    ~*python|curl|wget|scrapy     1;
    ~*bot|spider|crawl             2;
}

server {
    listen 80;
    server_name example.com;

    # User-Agent 过滤
    if ($is_bot = 1) {
        limit_req zone=cc_protect burst=10;
    }
    if ($is_bot = 2) {
        return 403;
    }

    # 动态限流(根据响应状态动态调整)
    limit_req_zone $binary_remote_addr zone=dynamic:50m rate=10r/s;

    # URI 限流
    location /api/ {
        limit_req zone=api_limit burst=50 nodelay;
        proxy_pass http://api_backend;
    }

    location /search {
        limit_req zone=search_limit burst=20 nodelay;
        proxy_pass http=backend;
    }

    # 静态资源允许较高并发
    location /static/ {
        limit_req zone=cc_protect burst=200 nodelay;
        proxy_pass http://static_backend;
        expires 7d;
        add_header Cache-Control "public, no-transform";
    }
}

 

6.3 自动封锁脚本

 

#!/bin/bash
# auto_block.sh - 自动封锁攻击 IP

LOG_FILE="/var/log/nginx/access.log"
BLACKLIST_FILE="/etc/nginx/blacklist.conf"
THRESHOLD=500  # 每分钟请求超过此值则封锁
WINDOW=1       # 时间窗口(分钟)

echo"$(date): 开始检测和封锁"

# 获取最近 N 分钟的高频 IP
CURRENT_MIN=$(date +"%d/%b/%Y:%H:%M")

# 使用 awk 统计并封锁
awk -v limit=$THRESHOLD -v blacklist=$BLACKLIST_FILE'
BEGIN {
    # 读取现有黑名单
    while ((getline line < blacklist) > 0) {
        blocked[line] = 1
    }
    close(blacklist)
}

{
    ip = $1
    requests[ip]++
}

END {
    for (ip in requests) {
        if (requests[ip] > limit && !blocked[ip]) {
            print ip >> blacklist
            print "封锁 IP:", ip, "请求数:", requests[ip]
            system("nginx -t && nginx -s reload")
        }
    }
}
'"$LOG_FILE"

echo"$(date): 检测完成"

 

7 综合配置示例

7.1 生产环境完整配置

 

# /etc/nginx/nginx.conf

user nginx;
worker_processes auto;
worker_rlimit_nofile 65535;

error_log /var/log/nginx/error.log warn;
pid /run/nginx.pid;

events {
    worker_connections 10240;
    use epoll;
    multi_accept on;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    # 日志格式
    log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                    '$status $body_bytes_sent "$http_referer" '
                    '"$http_user_agent" "$http_x_forwarded_for" '
                    'rt=$request_time uct="$upstream_connect_time" '
                    'uht="$upstream_header_time" urt="$upstream_response_time"';

    access_log /var/log/nginx/access.log main buffer=16k flush=5s;

    # 性能优化
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    keepalive_requests 1000;
    types_hash_max_size 2048;

    # 安全头部
    server_tokens off;
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-XSS-Protection "1; mode=block" always;

    # Gzip 压缩
    gzip on;
    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 6;
    gzip_types text/plain text/css text/xml application/json application/javascript application/rss+xml application/atom+xml image/svg+xml;

    # 限流区域定义
    limit_req_zone $binary_remote_addr zone=global:100m rate=100r/s;
    limit_req_zone $binary_remote_addr zone=api:50m rate=50r/s;
    limit_req_zone $binary_remote_addr zone=login:10m rate=1r/m;
    limit_req_zone $server_name zone=server_limit:100m rate=1000r/s;

    limit_conn_zone $binary_remote_addr zone=addr:50m;
    limit_conn_zone $server_name zone=server_conn:50m;

    # IP 名单
    geo $whitelist {
        default         0;
        10.0.0.0/8      1;
        172.16.0.0/12   1;
        192.168.0.0/16  1;
    }

    map $request_uri $rate_limit {
        default                         50r/s;
        ~* ^/api/v1/login              1r/m;
        ~* ^/api/v1/search             10r/s;
        ~* ^/admin                     10r/s;
        ~* ^/static                    500r/s;
    }

    # 封禁 IP 列表
    map $remote_addr $is_blocked {
        default         0;
        include         /etc/nginx/blocked.conf;
    }

    # 上游服务器
    upstream backend {
        server 127.0.0.1:8080 max_fails=3 fail_timeout=30s;
        server 127.0.0.1:8081 max_fails=3 fail_timeout=30s;
        keepalive 32;
    }

    upstream api_backend {
        server 127.0.0.1:9000 max_fails=3 fail_timeout=30s;
        server 127.0.0.1:9001 max_fails=3 fail_timeout=30s;
        keepalive 64;
    }

    server {
        listen 80;
        listen [::]:80;
        server_name example.com www.example.com;

        # 封禁 IP
        if ($is_blocked = 1) {
            return 403;
        }

        # 全局限流
        limit_req zone=global burst=200 nodelay;

        # 根路径
        location / {
            limit_req zone=api burst=100 nodelay;
            proxy_pass http://backend;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_http_version 1.1;
            proxy_set_header Connection "";
        }

        # API 接口
        location /api/ {
            limit_req zone=api burst=50 nodelay;
            limit_req_status 429;
            proxy_pass http://api_backend;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_http_version 1.1;
            proxy_set_header Connection "";
        }

        # 登录接口(严格限流)
        location /api/v1/login {
            limit_req zone=login burst=10 nodelay;
            limit_req_status 429;
            proxy_pass http://api_backend;
        }

        # 搜索接口
        location /api/v1/search {
            limit_req zone=api burst=30 nodelay;
            proxy_pass http://api_backend;
        }

        # 静态资源
        location /static/ {
            alias /var/www/static/;
            expires 7d;
            add_header Cache-Control "public, no-transform";
            limit_req zone=global burst=500 nodelay;
        }

        # 上传(严格限制大小和速度)
        location /upload {
            client_max_body_size 100m;
            client_body_timeout 300s;
            limit_req zone=login burst=5 nodelay;
            proxy_pass http://api_backend;
        }

        # 健康检查
        location /health {
            access_log off;
            return 200 "OK";
        }

        # 封禁敏感路径
        location ~ /. {
            deny all;
        }

        location ~ .(env|config|ini|log|sh|sql|conf|bak)$ {
            deny all;
        }

        # 错误页面
        error_page 429 /429.html;
        location = /429.html {
            root /usr/share/nginx/html;
        }

        error_page 502 503 504 /50x.html;
        location = /50x.html {
            root /usr/share/nginx/html;
        }
    }

    # HTTPS 配置
    server {
        listen 443 ssl http2;
        listen [::]:443 ssl http2;
        server_name example.com www.example.com;

        ssl_certificate /etc/nginx/ssl/example.com.crt;
        ssl_certificate_key /etc/nginx/ssl/example.com.key;
        ssl_protocols TLSv1.2 TLSv1.3;
        ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
        ssl_prefer_server_ciphers off;
        ssl_session_cache shared10m;
        ssl_session_timeout 1d;

        add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

        # 引用 HTTP 配置
        # 使用 include 复用上述 server 块配置
        include /etc/nginx/conf.d/*.conf;
    }

    # HTTP 重定向到 HTTPS
    server {
        listen 80;
        listen [::]:80;
        server_name example.com www.example.com;
        return 301 https://$server_name$request_uri;
    }
}

 

7.2 配置文件结构建议

 

# 推荐的文件结构
/etc/nginx/
├── nginx.conf              # 主配置
├── mime.types              # MIME 类型
├── conf.d/
│   ├── upstream.conf       # 上游服务器配置
│   ├── blacklist.conf      # 黑名单 IP
│   ├── whitelist.conf      # 白名单 IP
│   ├── blocked.conf        # 被封禁 IP
│   ├── rate_limit.conf    # 限流区域定义
│   ├── security.conf       # 安全配置
│   └── ssl.conf           # SSL 配置
└── ssl/
    ├── example.com.crt
    └── example.com.key

 

7.3 配置语法检查

 

#!/bin/bash
# test_nginx_config.sh - 验证 Nginx 配置

echo"=== Nginx 配置测试 ==="

# 测试主配置
nginx -t

if [ $? -eq 0 ]; then
    echo""
    echo"=== 配置测试通过 ==="
    echo""
    echo"=== 正在检查常用配置 ==="

    # 检查语法错误
    echo"1. 检查 conf.d/*.conf"
    for f in /etc/nginx/conf.d/*.conf; do
        if [ -f "$f" ]; then
            echo"   文件: $(basename $f)"
        fi
    done

    # 检查 SSL 证书
    echo""
    echo"2. SSL 证书检查"
    if [ -f /etc/nginx/ssl/example.com.crt ]; then
        echo"   证书: $(openssl x509 -in /etc/nginx/ssl/example.com.crt -noout -dates)"
    fi

    # 检查端口监听
    echo""
    echo"3. 端口监听检查"
    ss -tlnp | grep :80
    ss -tlnp | grep :443

    # 检查限流区域
    echo""
    echo"4. 限流区域"
    grep -E "limit_req_zone|limit_conn_zone" /etc/nginx/nginx.conf

    echo""
    echo"=== 所有检查完成 ==="
else
    echo"✗ 配置测试失败,请检查语法"
fi

 

8 监控与日志分析

8.1 限流统计

 

#!/bin/bash
# rate_limit_stats.sh - 限流统计

LOG_FILE="/var/log/nginx/error.log"

echo"=== Nginx 限流统计 ==="
echo""

# 统计限流次数
echo"[1] 限流触发次数(按类型]"
grep -c "limiting requests""$LOG_FILE" 2>/dev/null || echo"0"
grep -c "limiting connections""$LOG_FILE" 2>/dev/null || echo"0"

# 统计限流来源 TOP 10
echo""
echo"[2] 限流触发 TOP 10 IP"
grep "limiting requests""$LOG_FILE" 2>/dev/null | 
    awk '{print $NF}' | 
    sed 's/client: //' | 
    sort | uniq -c | sort -rn | head -10

# 统计被拒绝的 IP
echo""
echo"[3] 429 错误 TOP 10"
awk '$9 == "429" {print $1}' /var/log/nginx/access.log 2>/dev/null | 
    sort | uniq -c | sort -rn | head -10

# 统计 403 错误(可能被黑名单拦截)
echo""
echo"[4] 403 错误(可能拦截)TOP 10"
awk '$9 == "403" {print $1}' /var/log/nginx/access.log 2>/dev/null | 
    sort | uniq -c | sort -rn | head -10

# 统计限流原因
echo""
echo"[5] 限流原因分布"
grep "limiting requests""$LOG_FILE" 2>/dev/null | 
    grep -oE 'zone="[^"]+"' | 
    sort | uniq -c | sort -rn

 

8.2 Prometheus 指标

 

# Nginx 暴露 metrics(需要 nginx-module-vts 或 nginx-prometheus)
# 或者使用 access_by_lua 记录到 Prometheus

# 使用 Lua 模块记录指标
location /metrics {
    access_log off;
    # 限流指标
    limit_req_status 429;
    limit_conn_status 503;
}
# Prometheus 告警规则
groups:
-name:nginx_rate_limit_alerts
    rules:
      -alert:HighRateLimitRejections
        expr:|
          sum(rate(nginx_http_requests_total{status="429"}[5m]))
          / sum(rate(nginx_http_requests_total[5m])) * 100 > 10
        for:5m
        labels:
          severity:warning
        annotations:
          summary:"Nginx 限流拒绝率过高"
          description:"429 错误率超过 10%,当前值: {{ $value }}%"

      -alert:HighConnectionRejections
        expr:|
          sum(rate(nginx_http_requests_total{status="503"}[5m]))
          / sum(rate(nginx_http_requests_total[5m])) * 100 > 5
        for:5m
        labels:
          severity:critical
        annotations:
          summary:"Nginx 并发连接拒绝率过高"
          description:"503 错误率超过 5%,当前值: {{ $value }}%"

 

9 总结与检查清单

限流配置速查

 

# 限流区域定义
limit_req_zone $binary_remote_addr zone=name:10m rate=10r/s;
limit_conn_zone $binary_remote_addr zone=name:10m;

# 应用限流
limit_req zone=name burst=20 nodelay;
limit_req_status 429;

# 应用并发限制
limit_conn name 10;
limit_conn_status 503;

# 带宽限制
limit_rate_after 10m;
limit_rate 500k;

 

黑白名单速查

 

# 基本 IP 控制
allow 10.0.0.0/8;
deny all;

# geo 模块
geo $geo {
    default 0;
    10.0.0.0/8 1;
}

# map 模块
map $remote_addr $is_blocked {
    default 0;
    include /etc/nginx/blacklist.conf;
}

 

安全配置速查

 

# 隐藏版本
server_tokens off;

# 安全头部
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Strict-Transport-Security "max-age=31536000" always;

# 防止敏感文件
location ~ /. { deny all; }
location ~ .(env|config|ini|log)$ { deny all; }

# HTTPS
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;

 

排查检查清单

 

#!/bin/bash
# nginx_security_check.sh - 安全配置检查

echo"=========================================="
echo"    Nginx 限流与安全配置检查"
echo"=========================================="

echo""
echo"[1] 限流配置检查"
grep -E "limit_req_zone|limit_conn_zone" /etc/nginx/nginx.conf

echo""
echo"[2] 限流应用检查"
grep -E "limit_req|limit_conn" /etc/nginx/nginx.conf | head -20

echo""
echo"[3] 安全头部检查"
grep -E "server_tokens|X-Frame|X-Content|X-XSS|Strict-Transport" /etc/nginx/nginx.conf

echo""
echo"[4] SSL/TLS 配置"
grep -E "ssl_protocols|ssl_ciphers" /etc/nginx/nginx.conf

echo""
echo"[5] 黑名单检查"
if [ -f /etc/nginx/blocked.conf ]; then
    echo"已封锁 IP 数: $(wc -l < /etc/nginx/blocked.conf)"
    echo"最近封禁:"
    tail -5 /etc/nginx/blocked.conf
else
    echo"未配置黑名单文件"
fi

echo""
echo"[6] Nginx 版本"
nginx -v 2>&1

echo""
echo"[7] 配置语法测试"
nginx -t

echo""
echo"=========================================="

 

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分