Nginx同一端口HTTP跳转HTTPS的实现方法

背景

小目标:在只监听一个端口的情况下,将 HTTP 访问自动跳转为 HTTPS。

在常规情况下,HTTP 协议使用 80 端口,HTTPS 协议使用 443 端口。要实现 HTTP 强制跳转到 HTTPS 是一件非常简单的事情,随便都可以找到很多方案。然而,当使用非默认端口时,这就变得有点麻烦了。

场景描述

曾经有人通过在同一端口上同时监听 HTTP 和 HTTPS,实现 HTTP 和 HTTPS 在一个端口上工作。这种方式非常强大,但如果仅仅是为了让 HTTP 跳转到 HTTPS,有点杀鸡用牛刀的感觉。

解决方案

在研究 Nginx 的过程中,发现 Nginx 使用了一个特殊的状态码 497。根据 Nginx 的官方文档:

497:Nginx 内部使用的状态码,用于标识发送到 HTTPS 端口的纯 HTTP 请求,以区别于其他 4XX 错误,并用于错误页面重定向。

因此,可以利用 error_page 指令来拦截这个错误,实现同一端口的 HTTP 跳转到 HTTPS。

方法一:在 server 块中配置 error_page(局部配置)

实现步骤

  1. 修改 Nginx 配置

    在对应的 server 块中,添加以下配置:

    server {
        listen 443 ssl;
        listen [::]:443 ssl;
    
        server_name your_domain.com;
    
        ssl_certificate /path/to/fullchain.pem;
        ssl_certificate_key /path/to/privkey.pem;
    
        # 使用 error_page 实现同一端口的 HTTP 跳转到 HTTPS(局部配置)
        error_page 497 =301 https://$host:$server_port$request_uri;
    
        # 其他配置...
    }
    
  2. 变量解释

    • $host:请求中的主机名,可能包含端口号(如果非标准端口)。
    • $server_port:服务器监听的端口号。
    • $request_uri:完整的请求 URI,包括路径和查询字符串。
  3. 注意事项

    • 仅对当前 server 块生效:此配置只会影响定义了 error_pageserver 块,不会影响其他虚拟主机。
    • 适用于单个域名或站点:如果您只有一个站点需要此功能,可以使用此方法。

方法二:在 http 块中配置 error_page(全局配置)

实现步骤

  1. 修改 Nginx 配置

    http 块中,添加以下配置:

    http {
        # 使用 error_page 实现同一端口的 HTTP 跳转到 HTTPS(全局配置)
        error_page 497 =301 https://$host:$server_port$request_uri;
    
        # 其他全局配置...
    
        server {
            listen 443 ssl;
            listen [::]:443 ssl;
    
            server_name your_domain.com;
    
            ssl_certificate /path/to/fullchain.pem;
            ssl_certificate_key /path/to/privkey.pem;
    
            # 其他配置...
        }
    
        # 其他 server 块...
    }
    
  2. 变量解释

    同上。

  3. 注意事项

    • 作用于所有 server:此配置将影响所有虚拟主机,无论是否定义了 error_page 指令。
    • 适用于多个域名或站点:如果您有多个站点需要此功能,使用全局配置可以避免重复配置。

方法比较

方法一:局部配置 方法二:全局配置
作用范围 仅限于当前的 server 影响所有的 server
灵活性 可以针对特定的虚拟主机进行定制 对所有虚拟主机统一处理
配置简洁性 需要在每个 server 块中添加配置 只需在 http 块中配置一次
适用场景 仅有少数站点需要该功能,或需要不同的处理方式 所有站点都需要相同的处理方式

推荐使用全局配置

理由

最终配置示例

http {
    # 使用 error_page 实现同一端口的 HTTP 跳转到 HTTPS(全局配置)
    error_page 497 =301 https://$host:$server_port$request_uri;

    # 其他全局配置...

    server {
        listen 443 ssl;
        listen [::]:443 ssl;

        server_name your_domain.com;

        ssl_certificate /path/to/fullchain.pem;
        ssl_certificate_key /path/to/privkey.pem;

        # 其他配置...
    }

    # 其他 server 块...
}

变量解释

官方文档参考

注意事项

解决效果

配置完成并重启 Nginx 后,当用户以 HTTP 协议访问 HTTPS 端口时(例如 http://your_domain.com:443),服务器会自动将请求重定向到 https://your_domain.com:443,从而实现同一端口的 HTTP 到 HTTPS 跳转。

总结

通过使用 Nginx 的内部状态码 497,结合 error_page 指令,可以在同一端口上实现 HTTP 跳转到 HTTPS。两种配置方法各有优劣:

最终推荐使用全局配置,在 http 块中配置 error_page,这样可以确保所有站点都受益于同一端口 HTTP 到 HTTPS 的自动跳转,且配置管理更加方便。


示例完整配置

http {
    # 使用 error_page 实现同一端口的 HTTP 跳转到 HTTPS(全局配置)
    error_page 497 =301 https://$host:$server_port$request_uri;

    # 其他全局配置...

    server {
        listen 443 ssl;
        listen [::]:443 ssl;

        server_name your_domain.com;

        ssl_certificate /path/to/fullchain.pem;
        ssl_certificate_key /path/to/privkey.pem;

        # 其他配置...
    }

    # 其他 server 块...
}

参考资料

注:请将示例配置中的 your_domain.com 和证书路径替换为您自己的域名和证书路径。


PSerror_page 可以作为 server 节点的局部配置,也可以放在 http 节点作为全局配置。根据需求选择合适的配置方式,推荐使用全局配置以简化管理。