Web接入层的“铁三角”---防盗链、反向代理,负载均衡(nginx)

第一部分:防盗链(防别人白嫖你的资源)

场景

你的网站有商品图、视频、JS/CSS,结果被别人直接 <img src="https://你的域名/1.jpg"> 嵌到他网站上,流量全算你的,钱白花。

方案1:基础防盗链(靠 Referer 判断来源)

复制代码
# 文件位置:/etc/nginx/conf.d/anti-leech.conf

# 匹配所有图片、视频、静态资源
location ~* \.(jpg|jpeg|png|gif|bmp|mp4|avi|css|js|woff2)$ {

    # 【关键】定义哪些 Referer 是合法的
    # - none:用户直接在浏览器输入 URL(无 Referer)
    # - blocked:Referer 被代理/防火墙删了(比如公司内网)
    # - *.zjx521.com:你的主站和子站
    # - ~\.google\.:正则,允许 Google 爬虫(SEO 必须!)
    # - ~\.baidu\.:同理,允许百度
    valid_referers none blocked
                   *.zjx521.com zjx521.com
                   ~\.google\. ~\.baidu\. ~\.bing\.

    # 【关键】如果 Referer 不在上面列表里,就认为是盗链
    if ($invalid_referer) {
        # 方式1:直接返回 403 禁止访问(最省资源)
        return 403;

        # 方式2(可选):返回一张"禁止盗链"图片(用户体验好点)
        # rewrite ^/.*$ /static/forbidden.png break;
    }

    # 如果是合法请求,正常返回文件
    root /data/www/static;
    expires 30d;  # 浏览器缓存30天,减少回源
}

重要提醒

  • 这个方案能防小白,防不住高手(黑产用 Python requests 加个 fake Referer 就绕过);
  • 所以重要资源(比如付费视频)必须用方案2:Token 签名

方案2:高级防盗链(URL 带时效签名,也叫"URL 签名")

原理

生成一个带过期时间的 URL,比如:
https://cdn.yourdomain.com/video.mp4?expire=1712345678&sign=a1b2c3d4...

  • expire:Unix 时间戳,比如 5 分钟后过期;
  • sign:用密钥对 路径 + expire 做 MD5,别人算不出来。
配置(需 OpenResty,即 Nginx + Lua)
复制代码
# /usr/local/openresty/nginx/conf/conf.d/secure.conf

location /secure/ {
    access_by_lua_block {
        -- 引入 MD5 库(OpenResty 默认带)
        local md5 = require "resty.md5"

        local secret = "my_secret_2025"
        local uri = ngx.var.uri
        local expire = ngx.var.arg_expire
        local sign = ngx.var.arg_sign

        if not expire or not sign then
            ngx.exit(403)
        end

        if tonumber(expire) < ngx.time() then
            ngx.exit(403)
        end

        -- 正确计算 MD5
        local hasher = md5:new()
        hasher:update(secret .. uri .. expire)
        local expected_sign = hasher:final()

        -- 转成十六进制字符串(sign 通常是 hex)
        expected_sign = require("resty.string").to_hex(expected_sign)

        if sign ~= expected_sign then
            ngx.log(ngx.ERR, "Invalid sign")
            ngx.exit(403)
        end
    }

    alias /data/secure/;
}

实战经验

  • 密钥每天自动轮换,通过内部配置中心推送;
  • 签名链接生成逻辑放在业务代码里(Java/Go),前端只拿最终 URL;
  • CDN 也必须支持同样的签名逻辑(阿里云 CDN 支持,自建可用 Nginx 边缘节点)。

第二部分:反向代理(Nginx 当"中间人")

场景

用户访问 https://api.yourdomain.com,Nginx 把请求转给后端 Java 服务(比如 10.0.1.10:8080),但要加安全头、控超时、防雪崩

复制代码
# 文件位置:/etc/nginx/conf.d/api-proxy.conf

# 定义后端服务集群(多个实例)
upstream backend_api {
    # 【关键】健康检查 + 故障隔离
    server 10.0.1.10:8080 max_fails=2 fail_timeout=10s;
    server 10.0.1.11:8080 max_fails=2 fail_timeout=10s;

    # 【生产优化点】慢启动:新机器上线,先少分点流量,30秒后全量
    # server 10.0.1.12:8080 slow_start=30s;
}

server {
    listen 443 ssl http2;
    server_name api.yourdomain.com;

    # SSL 配置(略,参考前文)

    location / {
        # 【关键】透传用户真实 IP 给后端
        proxy_set_header Host $host;                # 原始 Host
        proxy_set_header X-Real-IP $remote_addr;    # 用户真实 IP
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # 链式代理 IP

        # 【关键】超时控制(防后端卡死拖垮 Nginx)
        proxy_connect_timeout 1s;   # 连接后端最多等1秒(阿里标准)
        proxy_send_timeout    3s;   # 发请求数据最多3秒
        proxy_read_timeout    5s;   # 等响应最多5秒(P99 要求)

        # 【关键】熔断重试:如果这个节点挂了,自动试下一个
        proxy_next_upstream error timeout http_502 http_503;
        proxy_next_upstream_timeout 2s;  # 重试总时间不超过2秒
        proxy_next_upstream_tries 3;     # 最多重试3次

        # 转发到 upstream
        proxy_pass http://backend_api;
    }
}

血泪教训 :一个后端 Full GC 30秒,Nginx 没设 proxy_read_timeout,连接池占满,整个 API 瘫痪。从此超时是强制基线


第三部分:负载均衡(四层 + 七层配合)

为什么不能只用 Nginx 做 LB?

  • Nginx 是七层(HTTP),每连接都要解析 HTTP 头,CPU 消耗大;
  • LVS 是四层(TCP),内核态转发,单机扛 100 万+ 连接。

三大负载均衡器对比

能力 LVS(四层) HAProxy(四/七层) Nginx(七层)
工作层级 传输层(TCP/UDP) 四层(TCP) + 七层(HTTP) 应用层(HTTP/HTTPS)
性能 ⭐⭐⭐⭐⭐(内核态,百万并发) ⭐⭐⭐⭐(用户态,10万级) ⭐⭐⭐(解析 HTTP,5万级)
配置难度 ❌ 极高(需调内核、ARP、VIP) ✅ 中等(纯配置文件) ✅ 简单
健康检查 简单 TCP 丰富(HTTP/SSL/TCP/Lua) 基础 HTTP
高可用 需 Keepalived 自带 peers 或 Keepalived 需外部 LB
适用场景 超大流量入口 中小网站、API 网关、内部服务 Web 服务器、反向代理
中小厂推荐度 ❌ 不推荐 ✅✅✅ 强烈推荐 ✅ 作为后端

一、大型/超大型网站架构

架构图

复制代码
用户 → [LVS(四层,IP: 10.0.0.100)] → [Nginx1, Nginx2, Nginx3] → [Java 服务]

步骤1:配置 LVS(用 Keepalived 实现高可用)

复制代码
# 文件:/etc/keepalived/keepalived.conf

# 虚拟 IP(VIP),用户访问这个 IP
vrrp_instance VI_API {
    state MASTER          # 主节点(备机写 BACKUP)
    interface eth0        # 绑定网卡
    virtual_router_id 51  # 同一组必须相同
    priority 100          # 主机优先级高
    advert_int 1          # 心跳间隔1秒

    authentication {
        auth_type PASS
        auth_pass 1111    # 心跳认证密码
    }

    virtual_ipaddress {
        10.0.0.100/24     # VIP,用户访问的 IP
    }
}

# 四层负载均衡规则
virtual_server 10.0.0.100 80 {
    delay_loop 6          # 健康检查间隔6秒
    lb_algo wrr           # 加权轮询(weight 越大流量越多)
    lb_kind DR            # DR 模式:回包不经过 LVS,性能最高!

    # 后端真实服务器(即 Nginx 节点)
    real_server 10.0.1.20 80 {
        weight 10         # 权重10
        TCP_CHECK {
            connect_timeout 3
            nb_get_retry 3
            delay_before_retry 3
        }
    }
    real_server 10.0.1.21 80 {
        weight 10
        TCP_CHECK { ... }
    }
}

步骤2:在 Nginx 节点绑定 VIP(DR 模式必需)

复制代码
# 在每台 Nginx 机器上执行(用 Ansible 自动化!)
ip addr add 10.0.0.100/32 dev lo      # 绑定 VIP 到 loopback
echo 1 > /proc/sys/net/ipv4/conf/lo/arp_ignore
echo 2 > /proc/sys/net/ipv4/conf/lo/arp_announce
echo 1 > /proc/sys/net/ipv4/conf/all/arp_ignore
echo 2 > /proc/sys/net/ipv4/conf/all/arp_announce

为什么这么麻烦?

DR 模式下,LVS 只改 MAC 地址,IP 不变。Nginx 必须"认领"这个 VIP,否则会丢包。

LVS、keepalived 待后续补充详解

传送门:。。。。。。。。

二、中小网站推荐架构:HAProxy + Nginx(无需 LVS)

复制代码
[用户]
  ↓ HTTPS
[HAProxy(四层 + 七层 LB,带 SSL 终止)]
  ↓ HTTP
[Nginx 集群(反向代理 + 静态资源)]
  ↓
[Java/Go 应用]

优势

  • HAProxy 一台机器就能做高可用(配 Keepalived);
  • 配置全是文本,Ansible 一键下发;
  • 支持 四层(TCP)透传 HTTPS七层(HTTP)终止 SSL,灵活选择。

三、HAProxy 详细配置(生产级,带注释)

场景:HTTPS 终止在 HAProxy,后端走 HTTP(减轻 Nginx CPU)

复制代码
# 文件:/etc/haproxy/haproxy.cfg

global
    log /dev/log local0 info
    maxconn 20000          # 最大连接数(根据机器调)
    tune.ssl.default-dh-param 2048  # 安全 DH 参数
    stats socket /run/haproxy/admin.sock mode 660 level admin

defaults
    log global
    mode http              # 默认七层模式
    option httplog         # 记录详细 HTTP 日志
    option dontlognull
    timeout connect 5s     # 连接后端超时
    timeout client  30s    # 客户端空闲超时
    timeout server  30s    # 后端响应超时
    timeout http-request 10s  # 读取 HTTP 请求头超时(防 Slowloris)
    retries 3              # 失败重试次数

# 【关键】健康检查模板
backend template
    option httpchk GET /health HTTP/1.1\r\nHost:\ api.yourdomain.com
    http-check expect status 200

# 前端:接收 HTTPS 流量
frontend https-in
    bind *:443 ssl crt /etc/ssl/yourdomain.pem  # 证书文件(含私钥+证书链)
    # ACL 判断 Host
    acl host_api hdr(host) -i api.yourdomain.com
    use_backend api-servers if host_api

# 后端:转发给 Nginx 集群
backend api-servers
    balance roundrobin     # 轮询(也可用 leastconn)
    server nginx1 10.0.1.20:80 check inter 2s fall 3 rise 2
    server nginx2 10.0.1.21:80 check inter 2s fall 3 rise 2
    # check:开启健康检查
    # inter 2s:每2秒检查一次
    # fall 3:连续3次失败才标记 down
    # rise 2:连续2次成功才标记 up

# 【可选】监控页面(生产环境加 ACL 限制 IP!)
listen stats
    bind *:8404
    stats enable
    stats uri /stats
    stats refresh 10s
    stats admin if TRUE

证书文件格式
yourdomain.pem 必须是 私钥 + 证书 + 中间 CA 拼在一起:

复制代码
cat yourdomain.key yourdomain.crt ca-bundle.crt > /etc/ssl/yourdomain.pem
chmod 600 /etc/ssl/yourdomain.pem

场景2:四层透传 HTTPS(SSL 终止在 Nginx)

如果你不想在 HAProxy 解密 HTTPS(比如合规要求),就用 TCP 模式:

复制代码
frontend https-tcp
    bind *:443
    mode tcp               # 四层模式
    default_backend nginx-tls

backend nginx-tls
    mode tcp
    balance leastconn      # TCP 连接用 leastconn 更公平
    server nginx1 10.0.1.20:443 check-ssl verify none  # verify none:跳过证书验证
    server nginx2 10.0.1.21:443 check-ssl verify none

适用场景

  • Nginx 需要拿到原始 TLS 信息(如 client cert);
  • 不想管理 HAProxy 的证书。

四、HAProxy + Nginx 协同要点

1. 透传真实 IP

HAProxy 默认不传 X-Real-IP,需手动加:

复制代码
frontend https-in
    ...
    http-request set-header X-Real-IP %[src]
    http-request set-header X-Forwarded-For %[src]
    http-request set-header X-Forwarded-Proto https

Nginx 侧无需改,直接用:

复制代码
proxy_set_header X-Real-IP $http_x_real_ip;

2. 高可用(Keepalived + HAProxy)

复制代码
# /etc/keepalived/keepalived.conf
vrrp_script chk_haproxy {
    script "killall -0 haproxy"  # 检查进程是否存在
    interval 2
    fall 2
    rise 2
}

vrrp_instance VI_1 {
    state MASTER
    interface eth0
    virtual_router_id 51
    priority 100
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    virtual_ipaddress {
        192.168.1.100/24   # 对外 VIP
    }
    track_script {
        chk_haproxy
    }
}

两台 HAProxy + Keepalived,成本低,配置简单,中小厂标配。

haproxy 待后续补充详解

传送门:。。。。。。。。


五、为什么不推荐中小厂用 LVS?

问题 LVS HAProxy
配置复杂度 需调内核参数、ARP、VIP 绑定 纯文本配置
故障排查 ipvsadm + tcpdump + 内核日志 日志清晰,/stats 页面可视化
功能扩展 几乎不能动态扩展 支持 Lua、ACL、自定义日志
运维成本 需专职网络工程师 普通 SRE 就能维护

教训 :配错 arp_ignore,导致整个 AZ 流量中断 20 分钟。从此非核心业务禁用 LVS。


行动建议

  1. 装 HAProxy(CentOS):

    复制代码
    sudo yum install -y haproxy keepalived
  2. 用上面的配置替换 /etc/haproxy/haproxy.cfg,记得:

    • yourdomain.pem 路径;
    • 改后端 Nginx IP;
    • /health 健康检查接口(返回 200 即可)。
  3. 启动并验证

    复制代码
    sudo systemctl start haproxy
    curl -H "Host: api.yourdomain.com" http://localhost:8404/stats  # 看状态
  4. 压测

    复制代码
    wrk -t12 -c1000 -d30s https://your-vip/api/test
    # 观察 HAProxy stats 页面的 sess_rate 和 error

现在立刻做这三件事:

  1. 防盗链测试

    复制代码
    curl -I -H "Referer: https://hacker.com" https://你的域名/logo.png
    # 正常应返回 HTTP/1.1 403 Forbidden
  2. 反向代理检查

    在 Nginx 日志格式加 $upstream_response_time,然后:

    复制代码
    awk '$NF > 5.0 {print $7, $NF}' /var/log/nginx/access.log
    # 找出响应时间 >5秒 的慢接口
  3. 负载均衡验证

    在 LVS 机器上执行:

    复制代码
    ipvsadm -ln  # 看 Real Server 状态是不是 Active
    ping 10.0.0.100  # 确认 VIP 能通

记住:生产环境没有"差不多"。防盗链少配一行,可能被薅秃;反向代理没设超时,一次 Full GC 就雪崩;负载均衡没做健康检查,一台机器挂了全站瘫痪。

相关推荐
C+ 安口木2 小时前
CSS通用优惠券样式
前端·css
小趴菜82272 小时前
安卓人机验证View
android·java·前端
珊瑚礁的猪猪侠2 小时前
Nginx从入门到精通:小白实战教程
运维·nginx
闲人编程3 小时前
2025年,如何选择Python Web框架:Django, Flask还是FastAPI?
前端·后端·python·django·flask·fastapi·web
光影少年3 小时前
react打包优化和配置优化都有哪些?
前端·react.js·掘金·金石计划
Aoda3 小时前
企业级项目结构设计的思考与实践 —— 以 PawHaven 为例
前端·架构
若无_3 小时前
深入理解 Vue 中的 reactive 与 ref:响应式数据的两种核心实现
前端·vue.js
邂逅星河浪漫3 小时前
【DockerFile+Nginx+DockerCompose】前后端分离式项目部署(docker容器化方式)
nginx·docker·centos·部署·docker-compose·dockerfile·容器化部署
玄魂3 小时前
一键生成国庆节祝福海报,给你的朋友圈上点颜色
前端·javascript·数据可视化