nginx配置在一个端口下,同时支持http与https两种协议

Nginx 不支持同一个端口 同时提供 HTTP 和 HTTPS 服务,原因如下:

  • HTTPHTTPS 使用的是不同的协议,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;
 }
 }
}

工作原理

  1. stream 监听 443 端口

    • 使用 ssl_preread 读取数据流,判断是否是 HTTPS(即是否有 TLS 握手)。
    • 如果是 HTTP,转发到 http_backend127.0.0.1:8080)。
    • 如果是 HTTPS,转发到 https_backend127.0.0.1:8443)。
  2. http {} 配置 HTTP 和 HTTPS

    • server 监听 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
  1. 客户端发送 HTTP 请求(明文)
  2. Nginx ssl_preread 检测到是 HTTP
  3. Nginx map 规则匹配$ssl_preread_protocol 为空 → 选择 http_backend
  4. 代理到 127.0.0.1:8080(HTTP 服务器)
  5. 服务器返回 HTTP 响应

? 情况 2:客户端访问 HTTPS

curl -v https://zhgdqh.ezczb.com:443
  1. 客户端发送 TLS ClientHello 握手包
  2. Nginx ssl_preread 读取 TLS 协议
  3. Nginx map 规则匹配:发现是 HTTPS → 选择 https_backend
  4. 代理到 127.0.0.1:8443(HTTPS 服务器)
  5. 服务器继续握手并建立 HTTPS 连接

? 为什么要这样做?

  1. 默认情况下,Nginx 不能在同一端口同时处理 HTTP 和 HTTPS

    • 传统方案:用 listen 80 处理 HTTP,listen 443 ssl 处理 HTTPS。
    • 但如果只能用 443 端口,就需要 ssl_preread 来区分流量。
  2. 适用于负载均衡或网关

    • 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 解决。

这个方案特别适合 ? 企业级 Nginx 反向代理、云服务器、Kubernetes Ingress 网关 ?

作者:禾火411原文地址:https://www.cnblogs.com/hehuo/p/18776436

%s 个评论

要回复文章请先登录注册