nginx配置在一个端口下,同时支持http与https两种协议
Nginx 不支持 在 同一个端口 同时提供 HTTP 和 HTTPS 服务,原因如下:
- HTTP 和 HTTPS 使用的是不同的协议,HTTPS 需要 TLS 握手,而 HTTP 不需要。
- Nginx 无法在同一个端口上自动区分 HTTP 和 HTTPS。
解决方案
虽然 Nginx 本身不支持 HTTP 和 HTTPS 共享同一端口,但你可以使用 SSL 预读(SSL Preread) 技术,通过 Nginx 的 stream
模块在 同一端口 上区分 HTTP 和 HTTPS 流量。
配置方式:使用 stream
模块监听 443 端口并区分 HTTP/HTTPS
# 在 http 之外的全局配置中
stream {
map $ssl_preread_protocol $name {
"" http_backend; # 没有 TLS 的是 HTTP
default https_backend; # 有 TLS 的是 HTTPS
}
upstream http_backend {
server 127.0.0.1:8080; # HTTP 服务器
}
upstream https_backend {
server 127.0.0.1:8443; # HTTPS 服务器
}
server {
listen 443;
proxy_pass $name;
ssl_preread on;
}
}
http {
server {
listen 8080;
server_name yourdomain.com;
location / {
root /usr/share/nginx/html;
index index.html;
}
}
server {
listen 8443 ssl;
server_name yourdomain.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
location / {
root /usr/share/nginx/html;
index index.html;
}
}
}
工作原理
stream
监听 443 端口- 使用
ssl_preread
读取数据流,判断是否是 HTTPS(即是否有 TLS 握手)。 - 如果是 HTTP,转发到
http_backend
(127.0.0.1:8080
)。 - 如果是 HTTPS,转发到
https_backend
(127.0.0.1:8443
)。
- 使用
http {}
配置 HTTP 和 HTTPSserver
监听8080
端口处理 HTTP。server
监听8443
端口处理 HTTPS。
最终效果
- 访问
http://yourdomain.com:443
:会自动转发到8080
端口的 HTTP 服务器。 - 访问
https://yourdomain.com:443
:会自动转发到8443
端口的 HTTPS 服务器。
这样,你就能在 一个端口(443) 上同时支持 HTTP 和 HTTPS 访问! ??
同一端口(443)支持 HTTP 和 HTTPS 访问的 Nginx 实现原理
Nginx 通过 stream
模块 和 ssl_preread
技术,在 443 端口 自动区分 HTTP 和 HTTPS,并将请求转发到不同的后台端口(8080 和 8443)。
? 主要技术点
1. stream
模块
- 作用:处理 TCP 层(四层) 代理,而不是 HTTP(七层)。
- 由于 Nginx 默认是 HTTP 服务器,只能解析 HTTP 请求,但
stream
允许它直接代理 TCP 流量(即 HTTP 和 HTTPS)。
2. ssl_preread on;
- 作用:在 不终止 TLS 连接 的情况下,读取客户端的 TLS 握手信息,从而判断是否为 HTTPS。
- 具体来说,它读取客户端发送的 ClientHello 数据包:
- 如果是 HTTPS ? 包含 TLS 版本、加密算法、SNI(服务器名称指示)
- 如果是 HTTP ? 直接是明文的 GET/POST 请求
3. map
变量映射
map $ssl_preread_protocol $proxy_backend {
"" http_backend; # 没有 TLS 的是 HTTP
default https_backend; # 有 TLS 的是 HTTPS
}
$ssl_preread_protocol
变量:- 为空("") ? 说明是 HTTP(明文)
- 非空 ? 说明是 HTTPS(加密)
- 作用:
- HTTP 请求? 代理到 8080 端口
- HTTPS 请求 ? 代理到 8443 端口
4. stream
服务器配置
server {
listen 443;
proxy_pass $proxy_backend;
ssl_preread on;
}
- 监听 443 端口
- 自动区分 HTTP 和 HTTPS
- 基于
$proxy_backend
变量,将请求转发到 8080(HTTP)或 8443(HTTPS)
? 访问流程
? 情况 1:客户端访问 HTTP
curl -v http://zhgdqh.ezczb.com:443
- 客户端发送 HTTP 请求(明文)
- Nginx
ssl_preread
检测到是 HTTP - Nginx
map
规则匹配:$ssl_preread_protocol
为空 → 选择http_backend
- 代理到
127.0.0.1:8080
(HTTP 服务器) - 服务器返回 HTTP 响应
? 情况 2:客户端访问 HTTPS
curl -v https://zhgdqh.ezczb.com:443
- 客户端发送 TLS
ClientHello
握手包 - Nginx
ssl_preread
读取 TLS 协议 - Nginx
map
规则匹配:发现是 HTTPS → 选择https_backend
- 代理到
127.0.0.1:8443
(HTTPS 服务器) - 服务器继续握手并建立 HTTPS 连接
? 为什么要这样做?
默认情况下,Nginx 不能在同一端口同时处理 HTTP 和 HTTPS
- 传统方案:用
listen 80
处理 HTTP,listen 443 ssl
处理 HTTPS。 - 但如果只能用 443 端口,就需要
ssl_preread
来区分流量。
- 传统方案:用
适用于负载均衡或网关
stream
代理 TCP 流量,可以在反向代理前端统一监听 443,然后转发到不同的服务(HTTP / HTTPS)。
? 方案优缺点
优点 | 缺点 |
---|---|
允许 HTTP 和 HTTPS 共用 443 端口 | stream 只能代理 TCP 层,无法解析 HTTP 请求路径 |
ssl_preread 无需解密 TLS,效率高 | 不能同时做 HTTP → HTTPS 自动重定向 |
兼容性好,适用于负载均衡 | 需要额外的 http {} 服务器监听 8080 / 8443 |
? 总结
✅ 通过 stream
+ ssl_preread
,Nginx 无需额外端口 就能在 443 端口同时支持 HTTP 和 HTTPS。
✅ 适用于负载均衡、网关、同一 IP 端口共用的场景。
✅ 高效,无需解密 HTTPS,直接转发 TCP 流量。
? 缺点:无法做 HTTP → HTTPS 自动跳转,但可以用前端 JS 或 meta refresh
解决。