在現代網路環境中,保護網站伺服器免受惡意攻擊和過量請求是至關重要的。Nginx 提供了強大的速率限制(Rate Limiting)功能,可以有效控制客戶端請求頻率,防止 DDoS 攻擊、暴力破解和資源濫用。本文將詳細介紹如何在 Ubuntu 22.04 上設定 Nginx 速率限制。
速率限制概念與用途
什麼是速率限制?
速率限制是一種控制機制,用於限制特定時間內來自單一來源的請求數量。當請求超過設定的閾值時,Nginx 會拒絕額外的請求或將其排入佇列等待處理。
速率限制的主要用途
- 防止 DDoS 攻擊:限制每個 IP 的請求頻率,減輕分散式阻斷服務攻擊的影響
- 保護 API 端點:避免 API 被過度使用或濫用
- 防止暴力破解:限制登入嘗試次數,保護使用者帳戶安全
- 資源保護:確保伺服器資源公平分配給所有使用者
- 成本控制:避免因流量激增而產生意外的頻寬費用
前置準備
確保您的 Ubuntu 22.04 系統已安裝 Nginx:
1
2
3
4
5
6
7
8
9
10
11
12
| # 更新套件清單
sudo apt update
# 安裝 Nginx
sudo apt install nginx -y
# 確認 Nginx 版本
nginx -v
# 啟動並設定開機自動啟動
sudo systemctl start nginx
sudo systemctl enable nginx
|
limit_req_zone 與 limit_req 指令
Nginx 的請求速率限制主要透過兩個指令來實現:limit_req_zone 和 limit_req。
limit_req_zone 指令
limit_req_zone 用於定義共享記憶體區域和速率限制規則,必須在 http 區塊中設定。
語法:
1
| limit_req_zone key zone=name:size rate=rate;
|
參數說明:
key:用於識別請求的鍵值,通常使用 $binary_remote_addr(客戶端 IP 的二進位格式)zone:共享記憶體區域的名稱和大小rate:允許的請求速率(每秒或每分鐘)
範例配置:
編輯 Nginx 主配置檔案:
1
| sudo nano /etc/nginx/nginx.conf
|
在 http 區塊中加入:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| http {
# 定義速率限制區域
# 每個 IP 每秒最多 10 個請求
limit_req_zone $binary_remote_addr zone=req_limit:10m rate=10r/s;
# 每個 IP 每分鐘最多 30 個請求(適用於 API)
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=30r/m;
# 基於 URI 的速率限制
limit_req_zone $request_uri zone=uri_limit:10m rate=1r/s;
# 組合鍵值(IP + URI)
limit_req_zone $binary_remote_addr$request_uri zone=combined_limit:10m rate=5r/s;
# 其他配置...
}
|
記憶體大小說明:
- 1MB 約可儲存 16,000 個 IP 地址的狀態
- 10MB 可處理約 160,000 個不同的 IP 地址
limit_req 指令
limit_req 用於在特定位置啟用速率限制,可在 http、server 或 location 區塊中使用。
語法:
1
| limit_req zone=name [burst=number] [nodelay | delay=number];
|
基本範例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| server {
listen 80;
server_name example.com;
# 對整個網站啟用速率限制
limit_req zone=req_limit;
location /api/ {
# 對 API 端點啟用更嚴格的速率限制
limit_req zone=api_limit;
proxy_pass http://backend;
}
location /login {
# 登入頁面使用較嚴格的限制
limit_req zone=api_limit;
}
}
|
Burst 與 nodelay 參數
Burst 參數
burst 參數允許請求在短時間內超過設定的速率,這些超額請求會被排入佇列等待處理,而不是立即被拒絕。
1
2
3
4
5
6
7
8
9
10
| http {
limit_req_zone $binary_remote_addr zone=req_limit:10m rate=10r/s;
}
server {
location / {
# 允許最多 20 個請求排隊
limit_req zone=req_limit burst=20;
}
}
|
工作原理:
- 速率設定為 10r/s,表示每 100ms 處理一個請求
- burst=20 表示最多可以有 20 個請求在佇列中等待
- 當佇列滿時,新的請求將收到 503 錯誤
nodelay 參數
預設情況下,佇列中的請求會按照設定的速率逐一處理,這可能導致明顯的延遲。nodelay 參數可以讓佇列中的請求立即處理。
1
2
3
4
5
6
| server {
location / {
# 立即處理 burst 中的請求,不延遲
limit_req zone=req_limit burst=20 nodelay;
}
}
|
使用 nodelay 的效果:
- 佇列中的請求會立即被處理
- 但仍會計入速率限制的計算
- 適合需要快速回應的應用場景
delay 參數
Nginx 1.15.7 之後引入了 delay 參數,提供更精細的控制:
1
2
3
4
5
6
| server {
location / {
# 前 12 個超額請求立即處理,之後的請求需要排隊
limit_req zone=req_limit burst=20 delay=12;
}
}
|
完整配置範例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
| http {
# 定義多個速率限制區域
limit_req_zone $binary_remote_addr zone=general:10m rate=10r/s;
limit_req_zone $binary_remote_addr zone=strict:10m rate=1r/s;
limit_req_zone $binary_remote_addr zone=api:10m rate=100r/m;
server {
listen 80;
server_name example.com;
# 一般頁面:較寬鬆的限制
location / {
limit_req zone=general burst=20 nodelay;
root /var/www/html;
}
# 敏感操作:嚴格限制
location /admin/ {
limit_req zone=strict burst=5;
proxy_pass http://admin_backend;
}
# API 端點:適中的限制
location /api/v1/ {
limit_req zone=api burst=10 delay=5;
proxy_pass http://api_backend;
}
}
}
|
連線數限制(limit_conn)
除了請求速率限制外,Nginx 還提供連線數限制功能,用於限制同時連線的數量。
limit_conn_zone 指令
1
2
3
4
5
6
7
| http {
# 定義連線數限制區域
limit_conn_zone $binary_remote_addr zone=conn_limit:10m;
# 基於伺服器的連線限制
limit_conn_zone $server_name zone=server_conn:10m;
}
|
limit_conn 指令
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| server {
listen 80;
server_name example.com;
# 每個 IP 最多 10 個同時連線
limit_conn conn_limit 10;
# 每個伺服器最多 1000 個同時連線
limit_conn server_conn 1000;
location /download/ {
# 下載區域限制每個 IP 最多 2 個同時連線
limit_conn conn_limit 2;
# 限制每個連線的下載速度為 500KB/s
limit_rate 500k;
}
}
|
結合速率限制與連線數限制
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| http {
# 速率限制區域
limit_req_zone $binary_remote_addr zone=req_limit:10m rate=10r/s;
# 連線數限制區域
limit_conn_zone $binary_remote_addr zone=conn_limit:10m;
server {
listen 80;
server_name example.com;
# 同時啟用兩種限制
limit_req zone=req_limit burst=20 nodelay;
limit_conn conn_limit 20;
location /api/ {
limit_req zone=req_limit burst=10 nodelay;
limit_conn conn_limit 5;
proxy_pass http://api_backend;
}
}
}
|
多層速率限制策略
在實際應用中,通常需要針對不同的端點和使用場景設定多層速率限制策略。
分層限制策略
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
| http {
# 全域基礎限制
limit_req_zone $binary_remote_addr zone=global:10m rate=50r/s;
# 登入相關的嚴格限制
limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m;
# API 限制
limit_req_zone $binary_remote_addr zone=api:10m rate=100r/m;
# 靜態資源的寬鬆限制
limit_req_zone $binary_remote_addr zone=static:10m rate=200r/s;
# 連線數限制
limit_conn_zone $binary_remote_addr zone=conn_per_ip:10m;
limit_conn_zone $server_name zone=conn_per_server:10m;
server {
listen 80;
server_name example.com;
# 全域限制
limit_req zone=global burst=100 nodelay;
limit_conn conn_per_ip 50;
limit_conn conn_per_server 10000;
# 登入頁面 - 最嚴格的限制
location = /login {
limit_req zone=login burst=3;
limit_conn conn_per_ip 3;
proxy_pass http://auth_backend;
}
# 密碼重設 - 同樣嚴格
location = /reset-password {
limit_req zone=login burst=2;
limit_conn conn_per_ip 2;
proxy_pass http://auth_backend;
}
# API 端點
location /api/ {
limit_req zone=api burst=20 delay=10;
limit_conn conn_per_ip 10;
proxy_pass http://api_backend;
}
# 靜態資源
location /static/ {
limit_req zone=static burst=50 nodelay;
alias /var/www/static/;
}
# 圖片資源
location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
limit_req zone=static burst=100 nodelay;
expires 1y;
add_header Cache-Control "public, immutable";
}
}
}
|
基於請求方法的限制
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
| http {
# GET 請求的限制(較寬鬆)
limit_req_zone $binary_remote_addr zone=get_limit:10m rate=30r/s;
# POST 請求的限制(較嚴格)
limit_req_zone $binary_remote_addr zone=post_limit:10m rate=5r/s;
server {
listen 80;
server_name example.com;
location /api/ {
# 使用 map 或 if 來區分請求方法
limit_req zone=get_limit burst=50 nodelay;
if ($request_method = POST) {
set $limit_rate_zone "post";
}
proxy_pass http://api_backend;
}
}
}
# 更優雅的方式:使用 map
http {
map $request_method $limit_key {
GET $binary_remote_addr;
POST $binary_remote_addr;
default $binary_remote_addr;
}
limit_req_zone $limit_key zone=method_limit:10m rate=10r/s;
}
|
白名單與例外處理
在某些情況下,您需要將特定 IP 或用戶端排除在速率限制之外。
使用 geo 模組設定白名單
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
| http {
# 定義白名單
geo $whitelist {
default 0;
# 本地主機
127.0.0.1 1;
# 內部網路
10.0.0.0/8 1;
192.168.0.0/16 1;
172.16.0.0/12 1;
# 特定的信任 IP
203.0.113.50 1;
198.51.100.0/24 1;
# 監控服務 IP
203.0.113.100 1;
}
# 基於白名單的速率限制鍵值
map $whitelist $limit_key {
0 $binary_remote_addr;
1 ""; # 空字串表示不限制
}
# 使用條件式鍵值
limit_req_zone $limit_key zone=req_limit:10m rate=10r/s;
limit_conn_zone $limit_key zone=conn_limit:10m;
server {
listen 80;
server_name example.com;
# 白名單 IP 不受限制
limit_req zone=req_limit burst=20 nodelay;
limit_conn conn_limit 20;
location / {
root /var/www/html;
}
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| http {
# 檢查 API Key
map $http_x_api_key $api_limit_key {
default $binary_remote_addr;
"your-premium-api-key-1" "";
"your-premium-api-key-2" "";
}
limit_req_zone $api_limit_key zone=api_limit:10m rate=100r/m;
server {
listen 80;
server_name api.example.com;
location /api/ {
limit_req zone=api_limit burst=20;
proxy_pass http://api_backend;
}
}
}
|
分級速率限制
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
| http {
# 根據 API 層級設定不同的限制
map $http_x_api_tier $rate_tier {
default "basic";
"premium" "premium";
"enterprise" "enterprise";
}
# 基礎用戶:100 請求/分鐘
limit_req_zone $binary_remote_addr zone=basic:10m rate=100r/m;
# 進階用戶:1000 請求/分鐘
limit_req_zone $binary_remote_addr zone=premium:10m rate=1000r/m;
# 企業用戶:10000 請求/分鐘
limit_req_zone $binary_remote_addr zone=enterprise:10m rate=10000r/m;
server {
listen 80;
server_name api.example.com;
location /api/ {
# 根據層級套用不同的限制
if ($rate_tier = "basic") {
limit_req zone=basic burst=10;
}
if ($rate_tier = "premium") {
limit_req zone=premium burst=100;
}
if ($rate_tier = "enterprise") {
limit_req zone=enterprise burst=1000;
}
proxy_pass http://api_backend;
}
}
}
|
日誌與監控
有效的日誌記錄和監控對於調整速率限制策略至關重要。
設定速率限制日誌
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
| http {
# 自訂日誌格式,包含速率限制資訊
log_format rate_limit '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'rt=$request_time uct="$upstream_connect_time" '
'limit_req_status=$limit_req_status';
# 速率限制區域
limit_req_zone $binary_remote_addr zone=req_limit:10m rate=10r/s;
# 設定被限制時的日誌級別
limit_req_log_level warn; # 可選:info, notice, warn, error
# 設定被拒絕請求的狀態碼
limit_req_status 429; # 預設是 503,建議使用 429 (Too Many Requests)
limit_conn_status 429;
server {
listen 80;
server_name example.com;
# 使用自訂日誌格式
access_log /var/log/nginx/access.log rate_limit;
error_log /var/log/nginx/error.log warn;
# 專門記錄被限制的請求
access_log /var/log/nginx/rate_limited.log rate_limit if=$limit_req_status;
limit_req zone=req_limit burst=20 nodelay;
location / {
root /var/www/html;
}
}
}
|
建立監控腳本
建立一個監控腳本來追蹤被限制的請求:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| #!/bin/bash
# /usr/local/bin/nginx-rate-limit-monitor.sh
LOG_FILE="/var/log/nginx/error.log"
ALERT_THRESHOLD=100
EMAIL="admin@example.com"
# 計算過去 5 分鐘內被限制的請求數
COUNT=$(grep "limiting requests" $LOG_FILE | \
awk -v date="$(date -d '5 minutes ago' '+%Y/%m/%d %H:%M')" \
'$0 >= date' | wc -l)
echo "過去 5 分鐘被限制的請求數: $COUNT"
# 如果超過閾值,發送警報
if [ $COUNT -gt $ALERT_THRESHOLD ]; then
echo "警告:速率限制觸發次數過高!" | \
mail -s "Nginx Rate Limit Alert" $EMAIL
fi
# 顯示被限制最多的 IP
echo "被限制最多的 IP 地址:"
grep "limiting requests" $LOG_FILE | \
grep -oE '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | \
sort | uniq -c | sort -rn | head -10
|
設定定時執行:
1
2
3
4
5
| # 設定 crontab
sudo crontab -e
# 每 5 分鐘執行一次監控
*/5 * * * * /usr/local/bin/nginx-rate-limit-monitor.sh >> /var/log/nginx/rate-limit-monitor.log 2>&1
|
使用 Prometheus 和 Grafana 監控
安裝 nginx-prometheus-exporter:
1
2
3
4
5
6
7
| # 下載並安裝 nginx-prometheus-exporter
wget https://github.com/nginxinc/nginx-prometheus-exporter/releases/download/v0.11.0/nginx-prometheus-exporter_0.11.0_linux_amd64.tar.gz
tar xzf nginx-prometheus-exporter_0.11.0_linux_amd64.tar.gz
sudo mv nginx-prometheus-exporter /usr/local/bin/
# 啟用 Nginx stub_status
sudo nano /etc/nginx/conf.d/status.conf
|
1
2
3
4
5
6
7
8
9
10
| # /etc/nginx/conf.d/status.conf
server {
listen 127.0.0.1:8080;
location /nginx_status {
stub_status on;
allow 127.0.0.1;
deny all;
}
}
|
即時監控命令
1
2
3
4
5
6
7
8
9
10
11
12
| # 即時監控被拒絕的請求
sudo tail -f /var/log/nginx/error.log | grep --line-buffered "limiting"
# 統計每分鐘被限制的請求數
watch -n 60 'grep "limiting requests" /var/log/nginx/error.log | \
grep "$(date "+%Y/%m/%d %H:%M")" | wc -l'
# 查看目前連線狀態
ss -s
# 查看每個 IP 的連線數
netstat -ntu | awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -rn | head -20
|
最佳實務與調校
速率限制設定建議
1. 從寬鬆開始,逐步收緊
1
2
3
4
5
| # 初始設定:較寬鬆的限制
limit_req_zone $binary_remote_addr zone=req_limit:10m rate=50r/s;
# 監控一段時間後,根據實際流量調整
# limit_req_zone $binary_remote_addr zone=req_limit:10m rate=20r/s;
|
2. 針對不同端點設定不同限制
1
2
3
4
5
6
7
8
9
10
11
12
13
| http {
# 靜態資源:高限制
limit_req_zone $binary_remote_addr zone=static:10m rate=100r/s;
# 動態頁面:中等限制
limit_req_zone $binary_remote_addr zone=dynamic:10m rate=20r/s;
# API:根據成本設定
limit_req_zone $binary_remote_addr zone=api:10m rate=60r/m;
# 認證相關:嚴格限制
limit_req_zone $binary_remote_addr zone=auth:10m rate=10r/m;
}
|
3. 使用適當的錯誤頁面
1
2
3
4
5
6
7
8
9
10
11
12
13
| server {
# 設定速率限制狀態碼
limit_req_status 429;
limit_conn_status 429;
# 自訂錯誤頁面
error_page 429 /429.html;
location = /429.html {
root /var/www/error;
internal;
}
}
|
建立自訂錯誤頁面:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
| <!-- /var/www/error/429.html -->
<!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="UTF-8">
<title>請求過於頻繁</title>
<style>
body {
font-family: Arial, sans-serif;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
background-color: #f5f5f5;
}
.container {
text-align: center;
padding: 40px;
background: white;
border-radius: 10px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
h1 { color: #e74c3c; }
p { color: #666; }
</style>
</head>
<body>
<div class="container">
<h1>429 - 請求過於頻繁</h1>
<p>您的請求次數過多,請稍後再試。</p>
<p>如果您認為這是錯誤,請聯繫網站管理員。</p>
</div>
</body>
</html>
|
4. 完整的生產環境配置範例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
| # /etc/nginx/nginx.conf
user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;
events {
worker_connections 1024;
use epoll;
multi_accept on;
}
http {
# 基本設定
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
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"';
log_format rate_limit '$remote_addr - [$time_local] "$request" '
'$status limit_req=$limit_req_status';
access_log /var/log/nginx/access.log main;
error_log /var/log/nginx/error.log warn;
# 白名單設定
geo $whitelist {
default 0;
127.0.0.1 1;
10.0.0.0/8 1;
192.168.0.0/16 1;
}
map $whitelist $limit_key {
0 $binary_remote_addr;
1 "";
}
# 速率限制區域
limit_req_zone $limit_key zone=general:10m rate=30r/s;
limit_req_zone $limit_key zone=api:10m rate=100r/m;
limit_req_zone $limit_key zone=auth:10m rate=10r/m;
limit_req_zone $limit_key zone=static:10m rate=100r/s;
# 連線數限制區域
limit_conn_zone $limit_key zone=conn_per_ip:10m;
limit_conn_zone $server_name zone=conn_per_server:10m;
# 限制設定
limit_req_log_level warn;
limit_req_status 429;
limit_conn_status 429;
# 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/xml application/xml+rss text/javascript;
# 載入網站配置
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
| # /etc/nginx/sites-available/example.com
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
# 重導向到 HTTPS
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name example.com www.example.com;
# SSL 設定
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
ssl_prefer_server_ciphers off;
# 文件根目錄
root /var/www/example.com;
index index.html index.htm;
# 全域速率限制
limit_req zone=general burst=50 nodelay;
limit_conn conn_per_ip 30;
limit_conn conn_per_server 10000;
# 日誌
access_log /var/log/nginx/example.com.access.log main;
access_log /var/log/nginx/example.com.ratelimit.log rate_limit if=$limit_req_status;
error_log /var/log/nginx/example.com.error.log warn;
# 錯誤頁面
error_page 429 /429.html;
location = /429.html {
root /var/www/error;
internal;
}
# 認證端點 - 最嚴格的限制
location ~ ^/(login|register|reset-password|forgot-password)$ {
limit_req zone=auth burst=3;
limit_conn conn_per_ip 3;
try_files $uri $uri/ =404;
}
# API 端點
location /api/ {
limit_req zone=api burst=20 delay=10;
limit_conn conn_per_ip 10;
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
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_cache_bypass $http_upgrade;
}
# 靜態資源
location /static/ {
limit_req zone=static burst=100 nodelay;
alias /var/www/example.com/static/;
expires 1y;
add_header Cache-Control "public, immutable";
}
# 圖片和媒體檔案
location ~* \.(jpg|jpeg|png|gif|ico|svg|webp|mp4|webm)$ {
limit_req zone=static burst=100 nodelay;
expires 1y;
add_header Cache-Control "public, immutable";
}
# CSS 和 JavaScript
location ~* \.(css|js)$ {
limit_req zone=static burst=100 nodelay;
expires 1y;
add_header Cache-Control "public, immutable";
}
# 預設位置
location / {
try_files $uri $uri/ =404;
}
}
|
測試與驗證
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| # 測試配置語法
sudo nginx -t
# 重新載入配置
sudo systemctl reload nginx
# 使用 ab (Apache Benchmark) 測試速率限制
ab -n 100 -c 10 http://example.com/
# 使用 wrk 進行更精確的測試
wrk -t4 -c100 -d30s http://example.com/
# 使用 curl 測試單一請求
for i in {1..20}; do
curl -s -o /dev/null -w "%{http_code}\n" http://example.com/
done
# 檢查日誌中的限制記錄
sudo grep "limiting" /var/log/nginx/error.log | tail -20
|
常見問題排解
速率限制不生效
- 確認
limit_req_zone 在 http 區塊中定義 - 檢查白名單配置是否意外跳過了限制
- 使用
nginx -t 確認配置語法正確
合法用戶被限制
- 增加
burst 值以容許突發流量 - 考慮使用
nodelay 參數 - 檢查是否有多個用戶共享同一 IP(NAT)
記憶體不足
- 增加 zone 的大小(如從 10m 改為 20m)
- 減少需要追蹤的鍵值數量
錯誤的狀態碼
- 確認已設定
limit_req_status 429 - 某些舊版 Nginx 可能不支援此設定
總結
Nginx 速率限制是保護網站和 API 的重要工具。透過本文介紹的配置方法,您可以:
- 使用
limit_req_zone 和 limit_req 控制請求頻率 - 使用
limit_conn_zone 和 limit_conn 控制同時連線數 - 透過
burst 和 nodelay 參數處理突發流量 - 設定白名單為信任的 IP 提供例外
- 建立完善的日誌和監控機制
- 根據不同端點需求設定多層限制策略
記住,速率限制需要根據實際流量模式進行調整。建議從較寬鬆的設定開始,持續監控並逐步優化,以達到安全與使用者體驗的最佳平衡。