271 lines
6.4 KiB
Markdown
271 lines
6.4 KiB
Markdown
---
|
||
title: Nginx 代理模式选择指南
|
||
description: 详细说明 Nginx Stream 模式和 HTTP 反向代理模式的区别、适用场景及常见问题解决方案
|
||
tags:
|
||
- nginx
|
||
- proxy
|
||
- traefik
|
||
- stream
|
||
- reverse-proxy
|
||
- mime-type
|
||
createdAt: 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 头信息的场景
|
||
|
||
**配置示例**:
|
||
```nginx
|
||
# /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 头的场景
|
||
|
||
**配置示例**:
|
||
```nginx
|
||
# /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 配置
|
||
|
||
```bash
|
||
# 如果使用了 stream 模式配置
|
||
sudo rm /etc/nginx/nginx.conf.d/stream/traefik.conf
|
||
# 或删除 nginx.conf 中的 stream {} 块
|
||
```
|
||
|
||
#### 步骤 2: 应用 HTTP 反向代理配置
|
||
|
||
```bash
|
||
# 复制配置文件
|
||
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: 验证
|
||
|
||
```bash
|
||
# 检查 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` 头
|
||
|
||
**解决**:
|
||
```nginx
|
||
proxy_set_header X-Real-IP $remote_addr;
|
||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||
```
|
||
|
||
### Q2: WebSocket 连接失败
|
||
|
||
**原因**: 没有配置协议升级
|
||
|
||
**解决**:
|
||
```nginx
|
||
proxy_http_version 1.1;
|
||
proxy_set_header Upgrade $http_upgrade;
|
||
proxy_set_header Connection "upgrade";
|
||
```
|
||
|
||
### Q3: HTTPS 证书错误
|
||
|
||
**原因**:
|
||
- HTTP 反向代理模式需要占位证书
|
||
- 或者 Traefik 后端使用自签名证书
|
||
|
||
**解决**:
|
||
```nginx
|
||
# 创建占位证书
|
||
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 反向代理模式。**
|
||
|
||
## 推荐配置
|
||
|
||
### 生产环境标准配置
|
||
|
||
```nginx
|
||
# /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;
|
||
}
|
||
```
|
||
|
||
## 参考资源
|
||
|
||
- [Nginx Stream Module 文档](http://nginx.org/en/docs/stream/ngx_stream_core_module.html)
|
||
- [Nginx HTTP Proxy Module 文档](http://nginx.org/en/docs/http/ngx_http_proxy_module.html)
|
||
- [Traefik 官方文档](https://doc.traefik.io/traefik/)
|