6.4 KiB
6.4 KiB
title, description, tags, createdAt
| title | description | tags | createdAt | ||||||
|---|---|---|---|---|---|---|---|---|---|
| Nginx 代理模式选择指南 | 详细说明 Nginx Stream 模式和 HTTP 反向代理模式的区别、适用场景及常见问题解决方案 |
|
2025-11-26 |
Nginx 代理模式选择指南
问题现象
当使用 Stream 模式转发时,浏览器会报错:
Refused to execute script from 'https://npm.xiongxiao.me/-/static/Home.854787d3346e44ccc262.js'
because its MIME type ('') is not executable, and strict MIME type checking is enabled.
原因分析
Nginx Stream 模式工作在 TCP/UDP 层(OSI 第 4 层),只做字节流转发,不解析 HTTP 协议,因此:
- ❌ 不会处理 HTTP 头信息(包括 Content-Type)
- ❌ 不会设置 X-Forwarded-* 头
- ❌ 不支持 WebSocket 协议升级
- ❌ MIME 类型信息丢失
- ✅ 转发效率更高(无需解析 HTTP)
两种模式对比
1. Stream 模式 (nginx-stream-proxy.conf)
工作层级: OSI 第 4 层 (TCP/UDP)
特点:
- ✅ 性能最优,CPU 占用低
- ✅ 适合纯 TCP/UDP 转发
- ❌ 不处理 HTTP 头
- ❌ 不支持基于 HTTP 的负载均衡
- ❌ 无法查看 HTTP 请求细节
适用场景:
- MySQL/PostgreSQL 数据库转发
- Redis/MongoDB 等数据库代理
- SSH/SFTP 端口转发
- 纯 TCP 协议转发
- 不需要 HTTP 头信息的场景
配置示例:
# /etc/nginx/nginx.conf 的 stream {} 块中
stream {
upstream traefik_http {
server 127.0.0.1:30080;
}
server {
listen 80;
proxy_pass traefik_http;
}
}
2. HTTP 反向代理模式 (nginx-traefik-proxy.conf) ⭐ 推荐
工作层级: OSI 第 7 层 (HTTP/HTTPS)
特点:
- ✅ 完整的 HTTP 协议支持
- ✅ 保留所有 HTTP 头信息(包括 Content-Type)
- ✅ 支持 WebSocket
- ✅ 可以设置自定义头
- ✅ 支持 SSL/TLS 终止
- ✅ 可以记录详细访问日志
- ⚠️ 性能略低于 Stream 模式(差异很小)
适用场景:
- Web 应用反向代理 ⭐
- API 网关
- 静态资源服务
- WebSocket 应用
- 需要处理 HTTP 头的场景
配置示例:
# /etc/nginx/conf.d/traefik-proxy.conf
server {
listen 80;
server_name _;
# 关键:保留 HTTP 头信息
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;
location / {
proxy_pass http://127.0.0.1:30080;
}
}
解决方案
当前问题:MIME Type 错误
原因: 使用了 Stream 模式,导致 Content-Type 头丢失
解决方法: 切换到 HTTP 反向代理模式
步骤 1: 停止当前 Nginx 配置
# 如果使用了 stream 模式配置
sudo rm /etc/nginx/nginx.conf.d/stream/traefik.conf
# 或删除 nginx.conf 中的 stream {} 块
步骤 2: 应用 HTTP 反向代理配置
# 复制配置文件
sudo cp nginx-traefik-proxy.conf /etc/nginx/conf.d/traefik-proxy.conf
# 创建 SSL 证书目录和占位证书(如果需要)
sudo mkdir -p /etc/nginx/ssl
sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout /etc/nginx/ssl/placeholder.key \
-out /etc/nginx/ssl/placeholder.crt \
-subj "/CN=placeholder"
# 测试配置
sudo nginx -t
# 重载 Nginx
sudo nginx -s reload
步骤 3: 验证
# 检查 HTTP 响应头
curl -I http://npm.xiongxiao.me
# 应该能看到正确的 Content-Type
# Content-Type: application/javascript; charset=utf-8
性能对比
Stream 模式
- 延迟: ~0.1ms
- 吞吐量: 接近网卡上限
- CPU 占用: 极低
- 内存占用: 极低
HTTP 反向代理模式
- 延迟: ~0.5-1ms
- 吞吐量: 95%+ 网卡性能
- CPU 占用: 低
- 内存占用: 低
结论: 对于 Web 应用,性能差异可以忽略不计,HTTP 反向代理模式是更好的选择。
常见问题
Q1: 为什么 Traefik 后端应用会收到错误的 IP?
原因: 没有设置 X-Real-IP 和 X-Forwarded-For 头
解决:
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
Q2: WebSocket 连接失败
原因: 没有配置协议升级
解决:
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
Q3: HTTPS 证书错误
原因:
- HTTP 反向代理模式需要占位证书
- 或者 Traefik 后端使用自签名证书
解决:
# 创建占位证书
sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout /etc/nginx/ssl/placeholder.key \
-out /etc/nginx/ssl/placeholder.crt \
-subj "/CN=placeholder"
# 信任后端自签名证书
proxy_ssl_verify off;
Q4: 什么时候用 Stream 模式?
答: 仅在以下场景使用 Stream 模式:
- 转发非 HTTP 协议(MySQL、Redis、SSH 等)
- 需要最极致的性能(每毫秒都很重要的场景)
- 纯 TCP/UDP 负载均衡
对于所有 Web 应用,请使用 HTTP 反向代理模式。
推荐配置
生产环境标准配置
# /etc/nginx/conf.d/traefik-proxy.conf
server {
listen 80;
listen [::]:80;
server_name _;
# 保留客户端信息
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 X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port;
# WebSocket 支持
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# 缓冲设置(根据实际调整)
proxy_buffering on;
proxy_buffer_size 4k;
proxy_buffers 8 4k;
proxy_busy_buffers_size 8k;
# 超时设置
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
location / {
proxy_pass http://127.0.0.1:30080;
proxy_redirect off;
}
# 日志
access_log /var/log/nginx/traefik-proxy.access.log;
error_log /var/log/nginx/traefik-proxy.error.log warn;
}