### 推荐标题

1. 服务器被DDoS打挂了?4层防线教你扛住流量洪峰

2. DDoS攻击来了怎么办?从免费到付费4层防御方案

3. 小公司也能防DDoS,这4招低成本高效果

今天把DDoS防御的4层防线整理出来,从免费到付费,从小公司到大规模,总有一层适合你。


第一层:内核参数调优(零成本)

在攻击流量还没到应用层之前,先让内核挡一波。这几个参数必须调:

# 开启SYN Cookie防SYN Flood
sysctl -w net.ipv4.tcp_syncookies=1

# 增大SYN队列长度
sysctl -w net.ipv4.tcp_max_syn_backlog=8192

# 减少SYN重试次数
sysctl -w net.ipv4.tcp_synack_retries=2

# 加快TIME_WAIT回收
sysctl -w net.ipv4.tcp_tw_reuse=1
sysctl -w net.ipv4.tcp_max_tw_buckets=5000

同时用iptables做基础速率限制:

# 单IP每秒最多20个新连接
iptables -A INPUT -p tcp --syn -m connlimit --connlimit-above 20 -j DROP

# 单IP每秒最多50个包
iptables -A INPUT -p tcp -m limit --limit 50/s --limit-burst 100 -j ACCEPT

这一层能挡住80%的脚本小子和小规模攻击,零成本,所有Linux服务器都应该配上。


第二层:Nginx限流与防刷(零成本)

一、隐藏敏感信息

1.1 为什么要隐藏信息

攻击者在攻击之前会先"踩点"——探测服务器版本、模块、操作系统。信息越少,攻击成本越高。Nginx 默认会在错误页面和响应头里暴露版本号:

# 默认的响应头
Server: nginx/1.24.0

# 默认的 404 页面
404 Not Found
nginx/1.24.0

攻击者看到 nginx/1.24.0,就能精准查找这个版本的已知漏洞。

1.2 隐藏版本号

# /etc/nginx/nginx.conf 的 http 块里

http {
    server_tokens off;
}

改完后效果:

项目

server_tokens on(默认)

server_tokens off

响应头

Server: nginx/1.24.0 Server: nginx

错误页面

底部显示 nginx/1.24.0

底部只显示 nginx

暴露信息

版本号 + 模块信息

仅服务器名称

⚠️ 注意: server_tokens off 只是隐藏版本号,不是真正的安全措施。它增加的是攻击成本,不是安全性本身。真正的安全是及时更新 Nginx 版本和系统补丁。

1.3 完全隐藏 Server 头

server_tokens off 还会暴露 Server: nginx,如果想完全去掉:

# 需要安装 headers-more 模块
sudo apt install nginx-extras
# 包含了 headers-more 模块,或从源码编译时添加 --add-module 参数。

# 在 nginx.conf 的 http 块里
http {
    more_clear_headers "Server";
    more_clear_headers "X-Powered-By";
    more_clear_headers "X-Runtime";
}

方法

效果

需要额外模块

server_tokens off

隐藏版本号,保留 Server: nginx

不需要

more_clear_headers

完全去掉 Server 头

headers-more

编译时修改源码 src/http/ngx_http_header_filter_module.c

改掉默认 Server 字符串

不需要(但需要编译)

1.4 添加安全响应头

# /etc/nginx/conf.d/example.conf 的 server 块里

server {
    # 防止 MIME 类型嗅探
    add_header X-Content-Type-Options "nosniff" always;

    # 防止点击劫持(页面不允许被 iframe 嵌套)
    add_header X-Frame-Options "SAMEORIGIN" always;

    # 浏览器 XSS 过滤(虽然现代浏览器已内置,加上更保险)
    add_header X-XSS-Protection "1; mode=block" always;

    # 内容安全策略(CSP)——限制资源加载来源
    add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:;" always;

    # HSTS——强制浏览器以后都用 HTTPS 访问
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

    # Referrer 策略——控制 Referer 头泄露
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;

    # 权限策略——控制浏览器功能(摄像头/麦克风/地理位置等)
    add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
}

安全头

作用

推荐值

X-Content-Type-Options

防止浏览器猜测文件类型

nosniff

X-Frame-Options

防止页面被 iframe 嵌套

SAMEORIGIN

X-XSS-Protection

启用浏览器 XSS 过滤

1; mode=block

Content-Security-Policy

限制资源加载来源

按需配置

Strict-Transport-Security

强制 HTTPS(HSTS)

max-age=31536000

Referrer-Policy

控制 Referer 泄露

strict-origin-when-cross-origin

Permissions-Policy

禁用不需要的浏览器功能

按需配置

💡 提示: always 参数很重要。不加 always 时,这些头只在 2xx/3xx 响应中添加;加了 always 后,4xx/5xx 错误页面也会带上安全头。攻击者经常故意请求错误页面来探测信息。

1.5 隐藏不必要的错误信息

# 不要把后端错误暴露给用户
location /api/ {
    proxy_pass http://127.0.0.1:3000;

    # 后端返回 500 时,显示自定义错误页而不是堆栈信息
    proxy_intercept_errors on;
    error_page 500 502 503 504 /50x.html;
}

# 自定义错误页面
location = /50x.html {
    root /var/www/errors;
    internal;
}

二、限速——limit_req 模块

2.1 为什么限速

不限速时,一个 IP 一秒发 1000 个请求,Nginx 全部转发到后端,后端直接被打满。限速就是给每个 IP 设一个请求频率上限,超过的请求直接拒绝。

攻击类型

不限速时

限速后

CC 攻击

每秒 1000 请求打满后端

每秒只放 10 个,其余 503

暴力扫描目录

爬虫秒级扫完所有路径

每秒 2 个请求,扫描成本翻 500 倍

恶意爬虫

疯狂抓取内容

限制到人类正常速度

API 滥用

恶意刷接口

超出频率直接拒绝

2.2 limit_req_zone 定义区域

# /etc/nginx/nginx.conf 的 http 块里

http {
    # 定义限速区域
    # $binary_remote_addr  以二进制格式存储客户端 IP(比文本省内存)
    # zone=req_limit:10m   共享内存区域名和大小,10MB 约可存储十万级 IP 的状态(取决于实际请求模式)
    # rate=10r/s           每个 IP 每秒最多 10 个请求

    limit_req_zone $binary_remote_addr zone=req_limit:10m rate=10r/s;
}

💡 提示: $binary_remote_addr 比 $remote_addr 省内存。文本格式的 IPv4 地址占 15 字节(如 192.168.1.100),二进制格式只占 4 字节。10MB 内存约可存 16 万个 IP 的状态。

2.3 limit_req 启用限速

# /etc/nginx/conf.d/example.conf

server {
    # 全局限速(所有 location 共享)
    limit_req zone=req_limit burst=20 nodelay;

    # 只对 API 限速
    location /api/ {
        limit_req zone=req_limit burst=20 nodelay;
        proxy_pass http://127.0.0.1:3000;
    }

    # 登录接口更严格
    location /api/login {
        limit_req zone=req_limit burst=5 nodelay;
        proxy_pass http://127.0.0.1:3000;
    }
}

2.4 burst 和 nodelay 详解

这是限速配置最容易搞混的部分:

参数

作用

不加的后果

burst=20

允许突发 20 个请求排队等待

超过 rate 的请求直接 503

nodelay

排队的请求不延迟,立即处理

排队请求会延迟处理,用户体验差

各种组合的效果:

配置

行为

rate=10r/s

(无 burst)

严格每秒 10 个,第 11 个立即 503

rate=10r/s burst=20

突发 30 个(10+20)能进入队列,但队列中的请求要等,超出 503

rate=10r/s burst=20 nodelay

突发 30 个立即处理不延迟,超出 503(推荐

rate=10r/s burst=20 delay=10

前 10 个立即处理,后 10 个延迟处理,超出 503

# 图解:rate=10r/s burst=20 nodelay 的效果
#
# 时间轴(秒)
# 0s:  30个请求同时来 → 前30个通过(10常规+20突发),第31个503
# 1s:  又来5个 → 全部通过(桶里还有10个额度)
# 2s:  又来15个 → 全部通过(用完突发桶)
# 3s:  又来15个 → 前10个通过,后5个503(突发桶空了)

⚠️ 注意: burst 设太大会让限速形同虚设。一般 API 接口 burst 设 5-20,普通页面设 20-50。登录等敏感接口 burst 设 3-5。简单记忆:burst 是‘突发窗口’,nodelay 是‘不要排队’。两者结合就是‘允许你偶尔超一点,但不让你的请求等待。

2.5 自定义限速响应

默认被限速时返回 503 Service Unavailable,可以改成 429 Too Many Requests(更符合 HTTP 语义):

http {
    limit_req_zone $binary_remote_addr zone=req_limit:10m rate=10r/s;
    limit_req_status 429;
}

server {
    limit_req zone=req_limit burst=20 nodelay;

    # 自定义 429 响应
    error_page 429 = @rate_limited;

    location @rate_limited {
        default_type application/json;
        return 429 '{"error": "Too Many Requests", "retry_after": 60}';
    }
}

2.6 多维度限速

不同接口限速不同:

http {
    # 全局限速:每秒 10 个请求
    limit_req_zone $binary_remote_addr zone=general:10m rate=10r/s;

    # API 限速:每秒 5 个请求
    limit_req_zone $binary_remote_addr zone=api:10m rate=5r/s;

    # 登录限速:每秒 1 个请求
    limit_req_zone $binary_remote_addr zone=login:10m rate=1r/s;

    # 下载限速:每秒 2 个请求
    limit_req_zone $binary_remote_addr zone=download:10m rate=2r/s;
}

server {
    # 全局限速
    limit_req zone=general burst=20 nodelay;

    location /api/ {
        limit_req zone=api burst=10 nodelay;
        proxy_pass http://127.0.0.1:3000;
    }

    location /api/login {
        limit_req zone=login burst=3 nodelay;
        proxy_pass http://127.0.0.1:3000;
    }

    location /download/ {
        limit_req zone=download burst=5 nodelay;
        proxy_pass http://127.0.0.1:3000;
    }
}

接口类型

rate

burst

说明

普通页面

10r/s

20

正常浏览

API 接口

5r/s

10

前端轮询

登录接口

1r/s

3

防暴力破解

下载接口

2r/s

5

防带宽滥用

搜索接口

3r/s

5

防恶意搜索

三、限制并发连接数——limit_conn 模块

3.1 limit_req vs limit_conn

模块

限制维度

适用场景

limit_req

请求频率(每秒几次)

CC 攻击、API 滥用

limit_conn

并发连接数(同时几个)

慢速攻击、连接耗尽

limit_req 限制"每秒发多少个请求",limit_conn 限制"同时保持多少个连接"。两者互补,都要开。

3.2 配置 limit_conn

# http 块
http {
    # 定义连接数限制区域
    # 每个IP最多同时保持 10 个连接
    limit_conn_zone $binary_remote_addr zone=conn_limit:10m;
}

# server 块
server {
    # 每个IP最多 10 个并发连接
    limit_conn conn_limit 10;

    # 整个虚拟主机最多 1000 个并发连接(防止单站占满所有连接)
    limit_conn_zone $server_name zone=perserver:10m;

    limit_conn perserver 1000;

    limit_conn_status 429;
}

3.3 慢速攻击防护

慢速攻击(Slowloris)的原理是:攻击者建立大量连接,但每个请求都发得极慢(比如每分钟发一个字节),占满服务器的连接数,正常用户连不上。

攻击类型

原理

Nginx 防护

Slowloris

大量半连接,请求头发得极慢

limit_conn + 超时配置

Slow POST

发大 body 但传得极慢

client_body_timeout

Slow Read

读取响应极慢

client_header_timeout

# http 块——超时配置
http {
    # 客户端请求头超时(60秒没发完请求头就断开)
    client_header_timeout 60s;

    # 客户端请求体超时(60秒没发完 body 就断开)
    client_body_timeout 60s;

    # 客户端响应超时(发送响应后60秒客户端没读完就断开)
    send_timeout 60s;

    # keepalive 超时(空闲连接65秒后断开)
    keepalive_timeout 65s;

    # 限制请求体大小(防止超大 POST 攻击)
    client_max_body_size 10m;

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

超时参数

默认值

推荐值

说明

client_header_timeout

60s

30s

请求头发送超时

client_body_timeout

60s

30s

请求体发送超时

send_timeout

60s

30s

响应发送超时

keepalive_timeout

75s

30-65s

空闲连接超时

client_max_body_size

1m

按需设置

请求体最大大小

四、IP 封禁——allow / deny

4.1 基本用法

# 允许特定 IP,拒绝其他所有
location /admin {
    allow 192.168.1.0/24;     # 允许内网
    allow 10.0.0.0/8;         # 允许 VPN
    allow 123.45.67.89;       # 允许特定公网 IP
    deny all;                 # 拒绝其他所有

    proxy_pass http://127.0.0.1:3000;
}

# 拒绝特定 IP,允许其他所有
location / {
    deny 1.2.3.4;             # 拒绝单个 IP
    deny 5.6.0.0/16;          # 拒绝一个网段
    allow all;                # 允许其他所有

    proxy_pass http://127.0.0.1:3000;
}

⚠️ 注意: allow 和 deny 按从上到下顺序匹配,匹配到就停止。所以 deny all 一定要放在最后,allow all 一定要放在最后(取决于策略)。

4.2 适用场景

场景

策略

后台管理页面

allow 内网 + VPN,deny all

内部 API

allow 内网,deny all

封禁已知恶意 IP

deny 恶意IP,allow all

只允许特定国家访问

allow 国家IP段,deny all(需要 GeoIP)

4.3 全局 IP 封禁

# /etc/nginx/conf.d/blocklist.conf

# 封禁列表(可以单独维护一个文件,方便更新)
deny 1.2.3.4;        # 扫描器
deny 5.6.7.8;        # 暴力破解
deny 10.20.30.0/24;  # 某机房段

# 在 server 块里引用
server {
    include /etc/nginx/conf.d/blocklist.conf;
    # ... 其他配置
}

五、Geo 模块——条件封禁

5.1 为什么用 geo 模块

allow/deny 适合封少数 IP。如果要根据 IP 段做不同的策略(比如国内放行、国外限制),手动写 allow/deny 列表会非常长。geo 模块可以给 IP 段打标签,然后在配置里根据标签做不同的处理。

5.2 geo 基本用法

# http 块
http {
    # 定义 IP 分类
    geo $allowed_ip {
        default        0;     # 默认不允许
        192.168.0.0/16 1;     # 内网允许
        10.0.0.0/8     1;     # VPN允许
        123.45.67.89   1;     # 特定IP允许
    }
}

# server 块
server {
    location /admin {
        if ($allowed_ip = 0) {
            return 403;
        }
        proxy_pass http://127.0.0.1:3000;
    }
}

5.3 封禁恶意 IP 段

http {
    geo $blocked_ip {
        default 0;  # 默认不封禁

        # 已知攻击源
        1.2.3.0/24    1;
        5.6.0.0/16    1;
        10.20.30.0/24 1;

        # 某云扫描器段
        100.200.0.0/16 1;
    }
}

server {
    if ($blocked_ip = 1) {
        return 403;
    }
}

5.4 使用 map 实现灵活控制

http {
    # 定义封禁列表
    geo $is_blocked {
        default 0;
        1.2.3.0/24    1;
        5.6.0.0/16    1;
    }

    # 用 map 定义不同 location 的封禁行为
    map $is_blocked$uri $block_action {
        default        0;    # 不封禁
        1/api/login    1;    # 封禁访问登录接口
        1/admin        1;    # 封禁访问后台
    }
}

server {
    location / {
        if ($block_action = 1) {
            return 403;
        }
        proxy_pass http://127.0.0.1:3000;
    }
}

💡 提示: geo 模块的查找是 O(1) 的(基于 radix tree),性能远好于正则匹配。大量 IP 段的封禁建议用 geo,不要用 if + 正则。

六、限制 HTTP 方法

6.1 只允许必要的方法

server {
    # 只允许 GET、POST、HEAD
    if ($request_method !~ ^(GET|POST|HEAD)$ ) {
        return 405;
    }

    # API 允许 GET、POST、PUT、DELETE、HEAD
    location /api/ {
        if ($request_method !~ ^(GET|POST|PUT|DELETE|HEAD)$ ) {
            return 405;
        }
        proxy_pass http://127.0.0.1:3000;
    }
}

方法

用途

是否需要

GET

获取资源

普通网站需要

POST

提交数据

表单/API 需要

HEAD

只取响应头

健康检查需要

PUT

上传/更新资源

RESTful API

DELETE

删除资源

RESTful API

TRACE

调试用

必须禁止

(XST 攻击)

CONNECT

代理隧道

必须禁止

OPTIONS

预检请求

CORS 需要

PATCH

部分更新

RESTful API

⚠️ 注意: TRACE 和 CONNECT 方法在生产环境必须禁止。TRACE 会导致跨站追踪(XST)攻击,CONNECT 可能被用来做代理跳板。

七、防止目录遍历和敏感文件泄露

7.1 禁止访问隐藏文件和敏感路径

server {
    # 禁止访问以 . 开头的隐藏文件(.git、.env、.htaccess 等)
    location ~ /\. {
        deny all;
        access_log off;
        log_not_found off;
    }

    # 禁止访问备份文件
    location ~ ~$ {
        deny all;
        access_log off;
        log_not_found off;
    }

    # 禁止访问 .git 目录(源码泄露)
    location ~ /\.git {
        deny all;
        access_log off;
        log_not_found off;
    }

    # 禁止访问 .env 文件(数据库密码等)
    location ~ /\.env {
        deny all;
        access_log off;
        log_not_found off;
    }

    # 禁止访问配置文件
    location ~ \.(sql|bak|backup|old|swp|tmp|log)$ {
        deny all;
        access_log off;
        log_not_found off;
    }
}

7.2 常见泄露路径

路径

风险

处理方式

/.git/

源码泄露

deny all

/.env

数据库密码、API Key 泄露

deny all

/.htaccess

Apache 配置泄露

deny all

/.htpasswd

密码文件泄露

deny all

/wp-admin/

WordPress 后台

IP 白名单

/phpmyadmin/

数据库管理工具

IP 白名单

/*.sql

数据库备份泄露

deny all

/*.bak

备份文件泄露

deny all

/*.log

日志文件泄露

deny all

🚨 警告: .git 目录泄露是常见的安全事故之一。攻击者通过 /.git/config 可以还原整个源代码仓库,包括数据库密码、API 密钥等。务必确保 /.git 被封禁。

7.3 关闭目录列表

server {
    # 关闭目录列表(如果 index 文件不存在,不显示目录内容)
    autoindex off;
}

默认就是 off,但确认一下没被意外打开。

八、防止图片盗链

8.1 什么是盗链

别人网站直接引用服务器上的图片 URL,用户访问他的网站,图片是从服务器加载的——带宽是你出的,流量是你付的。

8.2 配置防盗链

server {
    # 图片防盗链
    location ~ \.(jpg|jpeg|png|gif|webp|svg|mp4|mp3|woff|woff2)$ {
        # valid_referers:合法的来源
        # none:直接访问(浏览器地址栏输入URL)
        # blocked:Referer 被代理删除了
        # server_names:本站域名
        valid_referers none blocked server_names
            *.example.com example.com
            *.google.com;  # 允许搜索引擎

        if ($invalid_referer) {
            # 返回一张替代图片(或直接 403)
            return 403;
            # 或者返回一张"禁止盗链"的图片
            # rewrite ^/ /hotlink-denied.png last;
        }

        root /var/www/example;
    }
}

valid_referers 参数

含义

none

允许无 Referer(直接访问)

blocked

允许 Referer 被代理删除的请求

server_names

允许 Referer 中的域名匹配 server_name

*.example.com

允许特定域名的子域

example.com

允许特定域名

💡 提示: 如果图片需要被第三方 CDN 加载(比如又拍云、七牛云),把 CDN 域名加到 valid_referers 里。valid_referers 是基础防护,不能完全防止盗链,对高价值资源考虑用签名 URL。

九、防 DDoS 基础

9.1 DDoS 攻击类型

类型

原理

Nginx 能防吗

CC 攻击

大量 HTTP 请求打满后端

✅ limit_req

连接耗尽

大量连接占满 worker

✅ limit_conn

慢速攻击

慢速请求占满连接

✅ 超时配置

SYN Flood

TCP 半连接洪泛

❌ 内核层防护(sysctl)

UDP/ICMP 洪泛

带宽耗尽

❌ 需要 ISP/高防IP

放大攻击

利用 DNS/NTP 放大流量

❌ 需要 ISP/高防IP

⚠️ 注意: Nginx 是应用层(L7)服务器,只能防应用层 DDoS(CC/连接耗尽/慢速攻击)。网络层 DDoS(SYN Flood/UDP 洪泛)需要内核参数调优 + 云服务商高防 IP。Nginx 做不到的事情不要勉强。

9.2 Nginx 层面能做的

第一层:限速 + 限连接

http {
    # 限速
    limit_req_zone $binary_remote_addr zone=req_limit:10m rate=10r/s;
    # 限连接
    limit_conn_zone $binary_remote_addr zone=conn_limit:10m;

    # 限制每 IP 请求体大小
    client_body_buffer_size 8k;
    client_max_body_size 2m;

    # 超时
    client_header_timeout 30s;
    client_body_timeout 30s;
    send_timeout 30s;
}

server {
    limit_req zone=req_limit burst=20 nodelay;
    limit_conn conn_limit 10;

    limit_req_status 429;
    limit_conn_status 429;
}

第二层:限制请求头大小和数量

http {
    # 限制请求头缓冲区(防止超大请求头攻击)
    client_header_buffer_size 1k;
    large_client_header_buffers 4 8k;

    # 限制请求体缓冲区
    client_body_buffer_size 8k;
}

第三层:内核参数调优

# /etc/sysctl.d/99-ddos.conf

# SYN Flood 防护
net.ipv4.tcp_syncookies = 1                # 开启 SYN Cookie
net.ipv4.tcp_max_syn_backlog = 65535        # SYN 队列长度
net.ipv4.tcp_synack_retries = 2             # SYN-ACK 重试次数(默认5,减少可防 SYN Flood)

# 连接复用
net.ipv4.tcp_tw_reuse = 1                   # 复用 TIME_WAIT
net.ipv4.tcp_fin_timeout = 15               # FIN-WAIT-2 超时

# 连接追踪
net.netfilter.nf_conntrack_max = 1000000    # 连接追踪表大小
net.netfilter.nf_conntrack_tcp_timeout_established = 1800  # 已建立连接超时

# ICMP 防护
net.ipv4.icmp_echo_ignore_broadcasts = 1    # 忽略广播 ICMP
net.ipv4.icmp_ignore_bogus_error_responses = 1

# 反向路径过滤
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
# 应用配置
sudo sysctl -p /etc/sysctl.d/99-ddos.conf

9.3 集成 fail2ban 自动封禁

Nginx 限速能挡住超速请求,但恶意 IP 还是在不断尝试。fail2ban 可以监控 Nginx 日志,自动把超过阈值的 IP 用 iptables 封掉。如果不想手动维护 IP 黑名单,用 fail2ban 自动封禁是最省心的方案。
创建对应的 filter 文件,或使用 fail2ban 自带的 nginx-limit-req 过滤规则(如果存在)。

# 安装 fail2ban
sudo apt install fail2ban

# 创建 Nginx 限速 jail 配置
sudo tee /etc/fail2ban/jail.d/nginx-limit.conf << 'EOF'
[nginx-limit-req]
enabled = true
filter = nginx-limit-req
action = iptables-multiport[name=ReqLimit, port="http,https", protocol=tcp]
logpath = /var/log/nginx/error.log
findtime = 600
bantime = 7200
maxretry = 10

[nginx-botsearch]
enabled = true
filter = nginx-botsearch
action = iptables-multiport[name=BotSearch, port="http,https", protocol=tcp]
logpath = /var/log/nginx/access.log
findtime = 600
bantime = 7200
maxretry = 2
EOF

# 启动 fail2ban
sudo systemctl enable fail2ban
sudo systemctl restart fail2ban

参数

含义

推荐值

findtime

统计时间窗口(秒)

600(10 分钟)

maxretry

窗口内最大触发次数

10

bantime

封禁时长(秒)

7200(2 小时)

# 查看 fail2ban 状态
sudo fail2ban-client status nginx-limit-req

# 输出示例:
# Status for the jail: nginx-limit-req
# |- Filter
# |  |- Currently failed: 3
# |  |- Total failed:     156
# |  `- File list:        /var/log/nginx/error.log
# `- Actions
#    |- Currently banned: 5
#    |- Total banned:     23
#    `- Banned IP list:   1.2.3.4 5.6.7.8 9.10.11.12 13.14.15.16 17.18.19.20

# 手动解封某个 IP
sudo fail2ban-client set nginx-limit-req unbanip 1.2.3.4

💡 提示: fail2ban 和 Nginx 限速是互补关系:Nginx 限速挡住超速请求(返回 429),fail2ban 监控到某个 IP 频繁触发限速,直接用 iptables 封掉它的所有连接。两层防护,效果倍增。

十、SSL/TLS 安全加固

10.1 禁用不安全的协议和加密套件

server {
    listen 443 ssl http2;

    # 只允许 TLS 1.2 和 1.3
    ssl_protocols TLSv1.2 TLSv1.3;

    # 安全的加密套件
    ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;

    # 服务器优先选择加密套件
    ssl_prefer_server_ciphers on;

    # SSL 会话缓存
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;
    ssl_session_tickets off;

    # OCSP Stapling(让客户端不需要单独查询证书状态)
    ssl_stapling on;
    ssl_stapling_verify on;
    resolver 223.5.5.5 223.6.6.6 valid=300s;
    resolver_timeout 5s;
}

协议

是否安全

处理

SSLv2

❌ 不安全

禁用

SSLv3

❌ 不安全

禁用(POODLE)

TLSv1.0

❌ 不安全

禁用

TLSv1.1

❌ 不安全

禁用

TLSv1.2

✅ 安全

保留

TLSv1.3

✅ 安全

优先使用

10.2 HSTS 强制 HTTPS

server {
    # HSTS:告诉浏览器一年内只走 HTTPS
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
}

⚠️ 注意: preload 参数意味着域名会被加入浏览器的 HSTS 预加载列表。一旦加入,所有用户访问域名都强制 HTTPS,无法回退到 HTTP。只有在确认所有子域名都支持 HTTPS 后才加 preload

十一、完整安全加固配置模板

把以上所有安全措施整合到一份配置里:

# /etc/nginx/nginx.conf

user www-data;
worker_processes auto;
worker_rlimit_nofile 65535;

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

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

http {
    # ===== 基础安全 =====
    server_tokens off;
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 30s;

    # ===== 限制请求大小 =====
    client_header_buffer_size 1k;
    large_client_header_buffers 4 8k;
    client_body_buffer_size 8k;
    client_max_body_size 2m;

    # ===== 超时配置 =====
    client_header_timeout 30s;
    client_body_timeout 30s;
    send_timeout 30s;

    # ===== 限速 =====
    limit_req_zone $binary_remote_addr zone=req_limit:10m rate=10r/s;
    limit_req_status 429;

    # ===== 限连接 =====
    limit_conn_zone $binary_remote_addr zone=conn_limit:10m;
    limit_conn_status 429;

    # ===== IP 封禁 =====
    geo $blocked_ip {
        default 0;
        # 在这里添加要封禁的 IP 段
        # 1.2.3.0/24    1;
        # 5.6.0.0/16    1;
    }

    # ===== Gzip =====
    gzip on;
    gzip_comp_level 5;
    gzip_min_length 1k;
    gzip_types text/plain text/css text/javascript application/javascript application/json application/xml image/svg+xml;
    gzip_vary on;

    # ===== SSL 全局 =====
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;
    ssl_session_tickets off;

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

    include /etc/nginx/conf.d/*.conf;
}
# /etc/nginx/conf.d/example.conf

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

    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    # ===== 安全响应头 =====
    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 "strict-origin-when-cross-origin" always;
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
    add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:;" always;

    # ===== 限速 + 限连接 =====
    limit_req zone=req_limit burst=20 nodelay;
    limit_conn conn_limit 10;

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

    # ===== 只允许必要 HTTP 方法 =====
    if ($request_method !~ ^(GET|POST|HEAD|OPTIONS)$ ) {
        return 405;
    }

    # ===== 隐藏敏感文件 =====
    location ~ /\. {
        deny all;
        access_log off;
        log_not_found off;
    }

    location ~ ~$ {
        deny all;
        access_log off;
        log_not_found off;
    }

    location ~ \.(sql|bak|backup|old|swp|tmp|log|env)$ {
        deny all;
        access_log off;
        log_not_found off;
    }

    # ===== 后台管理——IP 白名单 =====
    location /admin {
        allow 192.168.0.0/16;
        allow 10.0.0.0/8;
        allow 123.45.67.89;
        deny all;
        proxy_pass http://127.0.0.1:3000;
    }

    # ===== 登录接口——更严格限速 =====
    location /api/login {
        limit_req zone=req_limit burst=3 nodelay;
        proxy_pass http://127.0.0.1:3000;
    }

    # ===== API =====
    location /api/ {
        limit_req zone=req_limit burst=10 nodelay;
        proxy_pass http://127.0.0.1:3000;
    }

    # ===== 图片防盗链 =====
    location ~ \.(jpg|jpeg|png|gif|webp|svg|mp4)$ {
        valid_referers none blocked server_names *.example.com;
        if ($invalid_referer) {
            return 403;
        }
        root /var/www/example;
        expires 30d;
    }

    # ===== 静态文件 =====
    location / {
        root /var/www/example;
        index index.html;
        try_files $uri $uri/ =404;
    }

    access_log /var/log/nginx/example.access.log;
    error_log  /var/log/nginx/example.error.log warn;
}

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

十二、安全验证清单

上线前对照这张清单逐项检查:

检查项

命令

期望结果

版本号已隐藏

curl -I https://example.com Server: nginx

(无版本号)

安全头已添加

curl -I https://example.com

包含 X-Frame-Options 等

限速生效

ab -n 100 -c 20 https://example.com/

超出部分返回 429

.git 已封禁

curl -I https://example.com/.git/config

403 或 404

.env 已封禁

curl -I https://example.com/.env

403 或 404

TRACE 方法已禁止

curl -X TRACE https://example.com

405

HSTS 已启用

curl -I https://example.com

包含 Strict-Transport-Security

HTTPS 强制跳转

curl -I http://example.com

301 到 https

后台 IP 白名单

非白名单 IP 访问 /admin

403

fail2ban 运行

sudo fail2ban-client status

活跃 jail 列表

部署完成后用这些命令跑一遍,确认防护措施都生效了。

十三、常见问题排查

13.1 限速配置不生效

可能原因

检查方法

limit_req_zone 写在 server 块里

必须写在 http 块

limit_req 写在了 http 块

必须写在 server 或 location 块

没有 reload

sudo nginx -s reload

zone 名不匹配

检查 limit_req 的 zone 名和 zone 定义一致

burst 太大

burst 太大等于没限速

走了 CDN

CDN 回源时 IP 是 CDN 的,需要用 $http_x_forwarded_for

13.2 CDN 场景下限速失效

如果网站套了 CDN(如阿里云 CDN、腾讯云 CDN),Nginx 看到的 $remote_addr 是 CDN 节点 IP,不是用户真实 IP,限速会变成"对整个 CDN 节点限速"。

# 方案:用 real_ip 模块获取真实 IP

http {
    # 信任的 CDN IP 段(以阿里云为例)
    set_real_ip_from 47.0.0.0/8;
    set_real_ip_from 39.0.0.0/8;
    set_real_ip_from 120.0.0.0/8;

    # 从哪个头取真实 IP
    real_ip_header X-Forwarded-For;

    # 递归去除信任的代理 IP
    real_ip_recursive on;
}

⚠️ 注意: set_real_ip_from 必须只包含信任的 CDN/代理 IP。如果设成 0.0.0.0/0,任何人都可以伪造 X-Forwarded-For 头绕过限速。

13.3 allow/deny 不生效

可能原因

检查方法

规则顺序错误

allow/deny 从上到下匹配,先匹配先生效

走了反向代理

$remote_addr

 是代理 IP,需配 real_ip

location 优先级冲突

正则 location 优先级高于前缀 location

13.4 fail2ban 不封禁

可能原因

检查方法

日志路径不对

确认 logpath 和 Nginx 实际日志路径一致

filter 正则不匹配

sudo fail2ban-regex /var/log/nginx/error.log /etc/fail2ban/filter.d/nginx-limit-req.conf

findtime 太短

调大 findtime

maxretry 太大

调小 maxretry

iptables 规则被覆盖

sudo iptables -L -n

 检查 fail2ban 链

十四、速查表

限速速查

指令

作用

推荐值

limit_req_zone ... rate=

定义限速区域和频率

10r/s

limit_req zone= ... burst=

启用限速

burst=20

nodelay

突发请求不延迟

limit_req_status

限速响应码

429

封禁速查

方法

适用场景

示例

allow/deny

少量 IP 白名单/黑名单

deny 1.2.3.4;
geo

 模块

大量 IP 段分类

geo $blocked { ... }
fail2ban

自动封禁高频攻击 IP

监控日志 + iptables 封禁

iptables

系统级 IP 封禁

iptables -A INPUT -s 1.2.3.4 -j DROP

安全头速查

安全头

推荐值

X-Content-Type-Options

nosniff

X-Frame-Options

SAMEORIGIN

Strict-Transport-Security

max-age=31536000; includeSubDomains

Referrer-Policy

strict-origin-when-cross-origin

Content-Security-Policy

按需配置

超时速查

参数

推荐值

作用

client_header_timeout

30s

请求头超时

client_body_timeout

30s

请求体超时

send_timeout

30s

响应发送超时

keepalive_timeout

30s

空闲连接超时

client_max_body_size

2m

请求体最大大小

防护层级速查

防护层

工具/配置

防护内容

应用层 L7

Nginx limit_req

CC 攻击、API 滥用

应用层 L7

Nginx limit_conn

连接耗尽、慢速攻击

应用层 L7

fail2ban

自动封禁恶意 IP

传输层 L4

iptables

端口扫描、IP 封禁

内核层

sysctl

SYN Flood、ICMP

网络层

云服务商高防 IP / CDN

大流量 DDoS

小结

Nginx 安全加固的核心逻辑:

  1. 隐藏信息

    ——server_tokens off + 安全响应头,减少攻击者可获取的信息

  2. 限速防刷

    ——limit_req 控制请求频率,CC 攻击和 API 滥用的第一道防线

  3. 限连接防耗尽

    ——limit_conn 控制并发连接数,慢速攻击和连接耗尽的防线

  4. IP 封禁

    ——allow/deny 手动封禁,geo 模块批量分类,fail2ban 自动封禁

  5. 敏感路径保护

    ——封禁 .git.env、备份文件,防止源码和密码泄露

  6. SSL 加固

    ——禁用旧协议,只留 TLS 1.2/1.3,启用 HSTS

💡 提示: 安全加固不是一次性的工作。新的攻击手法不断出现,定期检查 Nginx 版本更新、审查日志中的异常请求、更新 fail2ban 规则,是保持安全的必要工作。

一个原则:多层防护,纵深防御。不要只靠一层——限速 + 限连接 + IP 封禁 + fail2ban + 内核参数 + 高防 IP,每一层都挡一部分攻击,叠加起来才能扛住真实流量。


第三层:CDN+WAF(低成本)

当攻击流量超过你的服务器带宽时,就得靠CDN扛了。

国内主流选择:Cloudflare(免费版就够用)、阿里云CDN+WAF、腾讯云EdgeOne。

关键配置:

1. 隐藏源站IP — 这是前提,否则攻击者绕过CDN直打源站,CDN白接了

2. 开启Under Attack模式 — Cloudflare一键开启,给每个访问者加5秒JS挑战

3. WAF规则 — 拦截SQL注入、XSS、命令注入等应用层攻击

怎么确认源站IP是否泄露?

# 检查历史DNS记录
nslookup -type=any yourdomain.com

# 确保源站只允许CDN回源IP访问
iptables -A INPUT -p tcp --dport 443 -s 173.245.48.0/20 -j ACCEPT
iptables -A INPUT -p tcp --dport 443 -j DROP
场景 推荐方案 月成本
个人博客 内核调优+Nginx限流 0元
中小业务 +Cloudflare免费版 0元
电商/SaaS 阿里云WAF+CDN 500-2000元
大规模业务 DDoS高防IP 5000元起

第四层:专业DDoS防护(有成本但保命)

当攻击流量超过10Gbps,前三层都不够用了。这时候需要专业的DDoS清洗服务:


攻击来了的应急流程

1. 判断攻击类型 — tcpdump -i eth0 -c 100 -nn 抓包看是SYN Flood还是HTTP Flood

2. 启用已有防护 — 开启Under Attack模式,加iptables限流规则

3. 联系运营商 — 大流量攻击需要在上游清洗

4. 保留证据 — 抓包保存,后续分析和报警

5. 复盘 — 攻击结束后检查源站IP是否泄露,防护是否有效

DDoS不是防不住,而是你得提前做好准备。等攻击来了再想对策,已经晚了。

你的公司有没有被DDoS过?最后是怎么解决的?评论区分享一下经验,大家互相学习。

核心观点:DDoS防御是分层体系,从内核参数到CDN到专业清洗,4层防线逐级升级。核心是提前部署,而不是事后补救。

### 推荐标签

#DDoS防御 #网络安全 #流量清洗 #CDN防护 #iptables #Nginx限流 #运维实战

  • 阿里云DDoS高防 — 国内最主流,10Gbps起步,按天计费
  • 腾讯云大禹 — 弹性防护,攻击时自动扩容
  • Cloudflare Enterprise — 国际业务首选,全球Anycast网络
Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐