Nginx配置
本文分享一下本博客使用Nginx
反向代理的一些心得
主要涉及ssl,连接优化,vitepress路径处理和通用处理二级域名等。
二级域名
通用处理二级域名
通过map
和upstream
指令可以实现通过一个server块通用处理二级域名
# 1. 定义二级域名到 upstream 的映射表
map $subdomain $upstream_name {
default ""; # 默认空值(不转发)
"twikoo" "twikoo_backend"; # twikoo.dl-web.top → 8080
}
# 2. 定义 upstream 块(管理后端服务)
upstream twikoo_backend {
server localhost:8080;
keepalive 32; # 启用长连接
}
server {
listen 80;
listen 443 ssl http2;
# 同时匹配主域名和子域名
server_name dl-web.top ~^(?<subdomain>.+)\.dl-web\.top$;
# 公共配置
index index.php index.html index.htm default.php default.htm default.html;
access_log /www/sites/dl-web.top/log/access.log;
error_log /www/sites/dl-web.top/log/error.log;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
resolver 8.8.8.8 ipv6=off;
# HTTP 跳转 HTTPS
if ($scheme = http) {
return 301 https://$host$request_uri;
}
# SSL 配置
ssl_certificate /www/sites/dl-web.top/ssl/fullchain.pem;
ssl_certificate_key /www/sites/dl-web.top/ssl/privkey.pem;
ssl_protocols TLSv1.3 TLSv1.2 TLSv1.1 TLSv1;
ssl_ciphers 省略;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_stapling on;
ssl_stapling_verify on;
add_header Strict-Transport-Security "max-age=31536000";
# 错误页处理
error_page 497 https://$host$request_uri;
# 限流配置
limit_conn perserver 300;
limit_conn perip 25;
limit_rate 512k;
location / {
proxy_set_header Host $host; # 关键:传递原始域名(status.dl-web.top)给后端
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme; # 传递协议(http/https),避免后端误判
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port;
# 逻辑1:有效二级域名 → 转发到对应服务
if ($upstream_name != "") {
proxy_pass http://$upstream_name;
break; # 终止后续处理
}
# 逻辑2:无效二级域名(非主域名且无映射) → 直接返回404/502
if ($subdomain != "") { # $subdomain有值 → 是二级域名但未匹配映射
return 404; # 或 return 502; 按需选择
}
# 逻辑3:主域名 → 处理静态资源(仅当$subdomain为空时执行)
charset utf-8;
root /www/sites/dl-web.top/index/dist;
try_files $uri $uri.html $uri/ =404;
error_page 404 /404.html;
error_page 403 /404.html;
# 静态资源缓存(仅主域名生效)
location ~* ^/assets/ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}
# 新增:确保二级域名的所有路径都被转发到后端
location ~ ^/(assets|api|socket.io|dashboard|login) {
if ($upstream_name != "") {
proxy_pass http://$upstream_name$request_uri;
break;
}
}
# 3. 主域名的 /api 转发(子域名不生效)
location /api {
proxy_pass http://localhost:9123;
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_set_header X-Forwarded-Proto $scheme;
proxy_set_header Referer $http_referer;
}
}
# 独立的 status.dl-web.top 配置
server {
listen 80;
listen 443 ssl http2;
server_name status.dl-web.top;
# 共用 SSL 配置
ssl_certificate /www/sites/dl-web.top/ssl/fullchain.pem;
ssl_certificate_key /www/sites/dl-web.top/ssl/privkey.pem;
# 所有请求直接转发到 Uptime Kuma
location / {
proxy_pass http://localhost:3001;
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_set_header X-Forwarded-Proto $scheme;
}
}
server {
listen 80;
listen 443 ssl http2;
server_name umami.dl-web.top;
# 共用 SSL 配置
ssl_certificate /www/sites/dl-web.top/ssl/fullchain.pem;
ssl_certificate_key /www/sites/dl-web.top/ssl/privkey.pem;
# 所有请求直接转发到 Uptime Kuma
location / {
proxy_pass http://localhost:3000;
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_set_header X-Forwarded-Proto $scheme;
}
}
普通配置二级域名
这样配置可以避免相互之前产生影响
下面的示例就是我配置了status.dl-web.top
和umami.dl-web.top
两个子域名
# 独立的 status.dl-web.top 配置
server {
listen 80 ;
listen 443 ssl http2 ;
server_name status.dl-web.top;
# 共用 SSL 配置
ssl_certificate /www/sites/dl-web.top/ssl/fullchain.pem;
ssl_certificate_key /www/sites/dl-web.top/ssl/privkey.pem;
# 所有请求直接转发到 Uptime Kuma
location / {
proxy_pass http://localhost:3001;
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_set_header X-Forwarded-Proto $scheme;
}
}
server {
listen 80 ;
listen 443 ssl http2 ;
server_name umami.dl-web.top;
# 共用 SSL 配置
ssl_certificate /www/sites/dl-web.top/ssl/fullchain.pem;
ssl_certificate_key /www/sites/dl-web.top/ssl/privkey.pem;
# 所有请求直接转发到 Uptime Kuma
location / {
proxy_pass http://localhost:3000;
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_set_header X-Forwarded-Proto $scheme;
}
}
图片防盗链
在 Nginx 中配置图片防盗链,核心是通过限制请求来源(Referer
)来阻止其他网站非法引用你的图片资源,从而节省带宽并保护内容版权。以下是详细的配置方法:
一、防盗链原理
浏览器在请求资源时,会在 HTTP 头中携带 Referer
字段,记录该请求的来源页面 URL。Nginx 可以通过检测 Referer
字段,允许可信域名的请求,拒绝其他域名的请求(如返回 403 错误或替换为默认图片)。
二、基础配置:限制 Referer
在 Nginx 的配置文件(如 nginx.conf
或站点配置文件 xxx.conf
)中,针对图片资源(如 .jpg
、.png
等)添加防盗链规则:
配置示例:
server {
listen 80;
server_name yourdomain.com; # 你的域名
# 图片防盗链配置
location ~* \.(jpg|jpeg|png|gif|webp|bmp|svg)$ {
# 允许直接访问(Referer为空,如直接在浏览器输入图片URL)
# 允许可信域名(自己的网站域名)
valid_referers none blocked yourdomain.com *.yourdomain.com;
# 如果Referer不在允许列表中,执行以下操作
if ($invalid_referer) {
return 403; # 拒绝访问,返回403错误
# 或返回自定义图片:rewrite ^/ /forbidden.png break;
}
# 其他图片相关配置(可选)
expires 30d; # 缓存30天
add_header Cache-Control "public, max-age=2592000";
}
}
三、配置参数详解
location ~\* \.(jpg|jpeg|png|gif|webp|bmp|svg)$
- 匹配所有图片格式的 URL(
~*
表示不区分大小写),可根据需要添加其他格式(如.ico
、.avif
等)。
- 匹配所有图片格式的 URL(
valid_referers
定义允许的来源,支持以下值:none
:允许无Referer
的请求(如直接在浏览器输入图片 URL)。blocked
:允许Referer
被防火墙或代理隐藏的请求(Referer
为空或不含协议)。yourdomain.com
:允许来自本域名的请求。*.yourdomain.com
:允许来自所有子域名的请求(如blog.yourdomain.com
)。- 其他可信域名:如
trusted.com *.trusted.com
(允许合作网站引用)。
if ($invalid_referer)
- 当Referer不在valid_referers列表中时,$invalid_referer为true执行内部逻辑:
return 403
:直接拒绝,返回 “403 Forbidden”。rewrite ^/ /forbidden.png break
:跳转到自定义的 “防盗链提示图”(需提前准备forbidden.png
并放在网站根目录)。
- 当Referer不在valid_referers列表中时,$invalid_referer为true执行内部逻辑:
四、进阶配置:更灵活的规则
1. 允许多个可信域名
如果需要允许多个外部域名引用(如合作网站),可在 valid_referers
中添加:
valid_referers none blocked yourdomain.com *.yourdomain.com trusted1.com;
2. 对非法请求返回默认图片
替代 403 错误,返回自定义提示图(更友好):
if ($invalid_referer) {
rewrite ^/ /anti-hotlink.png break; # 替换为你的提示图路径
}
需确保 anti-hotlink.png
放在 Nginx 配置的 root
目录下(如 /usr/share/nginx/html
)。
3. 针对 HTTPS 站点
如果网站启用了 HTTPS(listen 443 ssl
),Referer
可能包含 https://
,配置无需额外修改(valid_referers
会自动匹配协议)。
五、注意事项
Referer
的局限性Referer
可被伪造,无法完全杜绝防盗链,但能阻止绝大多数普通盗链。- 部分浏览器或隐私模式可能不发送
Referer
,因此valid_referers
中建议保留none
和blocked
。
静态资源服务器 如果图片放在独立的静态资源域名(如
img.yourdomain.com
),需在该域名的 Nginx 配置中单独添加防盗链规则。配置生效 修改后需重启或重载 Nginx 使配置生效:
nginx -t # 检查配置是否有误
systemctl restart nginx # 重启Nginx(CentOS/Ubuntu)
# 或 service nginx reload
六、测试防盗链效果
- 合法引用:在
yourdomain.com
的页面中引用图片,应正常显示。 - 非法引用:在其他域名(如
otherdomain.com
)的页面中引用图片,应返回 403 或自定义提示图。 - 直接访问:在浏览器地址栏输入图片 URL(无
Referer
),应正常显示(如果配置了none
)。
通过以上配置,可有效阻止大部分非法盗链行为,保护图片资源。根据实际需求,可调整允许的域名和非法请求的处理方式。
防盗链进阶
Referer
头可以被轻易伪造,因此基于 Referer
的防盗链机制只能阻止普通用户的盗链行为,无法防范专业攻击者。若要进一步增强安全性,可以结合以下方法:
一、使用 HTTP 签名(防篡改)
为图片 URL 添加签名参数,验证请求合法性:
实现原理
服务端生成签名: 在生成图片 URL 时,计算一个包含时间戳、图片路径的签名,并附加到 URL 中。
# Python示例:生成带签名的URL
import hmac
import hashlib
import time
def generate_signed_url(path, secret_key, expires=3600):
timestamp = int(time.time()) + expires # 签名有效期
string_to_sign = f"{path}{timestamp}"
signature = hmac.new(secret_key.encode(), string_to_sign.encode(), hashlib.sha256).hexdigest()
return f"https://yourdomain.com{path}?t={timestamp}&s={signature}"
Nginx 验证签名: 使用 ngx_http_lua_module
或 ngx_http_map_module
验证签名:
location /images/ {
# 提取参数
map $request_uri $path {
"~^(?<p>/images/[^?]+)" $p;
}
map $arg_t $timestamp {}
map $arg_s $signature {}
# 验证时间戳(防过期)
if ($timestamp < $msec) {
return 403;
}
# 验证签名(与服务端计算逻辑一致)
set $string_to_sign "$path$timestamp";
set $my_signature ""; # 通过Lua或其他方式计算签名
if ($my_signature != $signature) {
return 403;
}
}
优点
- 无法伪造 URL,必须知道密钥才能生成有效签名。
- 可设置 URL 有效期(如 30 分钟后失效),适合临时访问。
二、使用 Cookie 或 Session(精准验证)
通过用户会话验证请求合法性:
实现方式
用户登录后设置 Cookie:
# 用户登录时设置加密Cookie
response.set_cookie('auth_token', encrypt(user_id), expires=3600)
Nginx 验证 Cookie:
location /images/ {
# 检查Cookie是否存在且有效
access_by_lua_block {
local auth_token = ngx.var.cookie_auth_token
if not auth_token or not verify_token(auth_token) then
return ngx.exit(403)
end
}
}
变种:JSON Web Token (JWT)
# 使用ngx_http_auth_jwt_module验证JWT
location /images/ {
auth_jwt "Restricted";
auth_jwt_key_file /path/to/public_key.pem;
}
三、基于用户行为分析(防爬虫)
通过分析请求模式识别异常访问:
实现方法
IMPORTANT
limit_req_zone
不是写在server
块内,而是写在http
块内
限制请求频率:
# 限制每个IP每分钟最多请求100次图片
limit_req_zone $binary_remote_addr zone=img_limit:10m rate=100r/m;
location /images/ {
limit_req zone=img_limit;
}
检测异常请求模式:
- 同一 IP 短时间内请求大量不同图片。
- 非浏览器 UA(如
curl
、wget
)请求图片。
# 禁止非浏览器UA访问
if ($http_user_agent !~* (Chrome|Firefox|Safari|Edge)) {
return 403;
}
四、使用客户端渲染(防直接抓取)
将图片 URL 隐藏在 JavaScript 中动态生成:
实现示例
<!-- HTML中不直接暴露真实图片URL -->
<div id="image-container"></div>
<script>
// 通过JavaScript动态加载图片(带签名或token)
fetch('/api/get-image-url?path=/images/test.jpg')
.then(res => res.json())
.then(data => {
const img = new Image();
img.src = data.signed_url; // 如:/images/test.jpg?t=123&s=abc
document.getElementById('image-container').appendChild(img);
});
</script>
优点
- 爬虫难以直接获取真实图片 URL。
- 可结合用户会话进一步验证。
五、使用 Cloudflare 等 CDN 安全功能
CDN 提供商通常提供更高级的防盗链功能:
Cloudflare 配置示例
- 启用 Hotlink Protection: 在 Cloudflare 控制面板中,进入 Rules → Hotlink Protection,配置允许的域名。
- 使用 Cloudflare Access: 对特定路径设置身份验证,仅允许授权用户访问。
- WAF 规则: 配置自定义 WAF 规则,阻止异常图片请求。
六、图片水印(终极方案)
对图片添加不可见水印或显著水印,降低被盗用的价值:
实现方法
服务端动态添加水印:
from PIL import Image, ImageDraw, ImageFont
def add_watermark(img_path, text):
img = Image.open(img_path)
draw = ImageDraw.Draw(img)
font = ImageFont.truetype("arial.ttf", 36)
draw.text((10, 10), text, font=font)
return img
使用 Nginx 模块: 如 ngx_http_image_filter_module
或第三方模块。
七、综合方案推荐
- 初级防护:
- 基础 Referer 验证(防普通盗链)。
- 图片水印(降低盗用价值)。
- 中级防护:
- 签名 URL(防 URL 篡改)。
- 频率限制(防爬虫)。
- 高级防护:
- 用户会话验证(精准控制)。
- CDN 安全功能 + WAF(抵御大规模攻击)。
八、权衡安全性与性能
增强安全性通常会增加系统复杂度和性能开销,需根据实际需求选择:
- 普通网站:Referer + 水印 + 频率限制 即可。
- 高价值内容:考虑签名 URL + 用户认证 + CDN。
- 极端安全需求:结合多种方法,并定期更换密钥。