nginx.conf

nginx.conf

我想知道 nginx 是否能够处理 http 和 https 请求在同一端口上。 [*]

这就是我想做的事情。我正在运行一个处理 http 请求的 Web 服务器 (lighttpd) 和一个通过 https 为文档树的特定部分提供服务的 C 程序。这两个进程在同一台服务器上运行。

在防火墙层面,我只能有一个端口转发流量到该服务器。因此,我想要做的是在此服务器上设置 nginx,以便它在单个端口上监听请求,然后:

A)重定向所有http://myhost.com/* 请求以便它们转到 localhost:8080(lighttpd 正在监听的位置)

B) 如果用户请求以 https://myhost.com/app 开头的 URL,它会将该请求发送到 localhost:8008(C 程序)。请注意,在这种情况下,远程浏览器和 nginx 之间的流量必须加密。

您认为这可能吗?如果可能,该怎么做?

我知道如何使用两个不同的端口来实现这一点。我面临的挑战是只使用一个端口来实现这一点(不幸的是,我无法控制这个特定环境中的防火墙配置,因此这是我无法避免的限制)。使用通过 ssh 进行反向端口转发等技术来绕过防火墙也行不通,因为这应该适用于只有 Web 浏览器和 Internet 链接的远程用户。

如果这超出了 nginx 的能力,您知道有其他产品可以满足此要求吗?(到目前为止,我使用 lighttpd 和 pound 进行设置都失败了)。我还希望避免使用 Apache(尽管如果它是唯一可能的选择,我愿意使用它)。

提前致谢,亚历克斯

[*] 需要说明的是,我说的是处理加密通过同一端口的未加密 HTTP 连接。加密是通过 SSL 还是 TLS 完成并不重要。

答案1

根据维基百科关于状态代码的文章,当 http 流量发送到 https 端口时,Nginx 有一个自定义错误代码(错误代码 497)

并且根据nginx 文档中的 error_page,您可以定义针对特定错误显示的 URI。
因此,我们可以创建一个 URI,当出现错误代码 497 时,客户端将被发送到该 URI。

nginx.conf

    #lets assume your IP address is 89.89.89.89 and also that you want nginx to listen on port 7000 and your app is running on port 3000

    server {
        listen 7000 ssl;
     
        ssl_certificate /path/to/ssl_certificate.cer;
        ssl_certificate_key /path/to/ssl_certificate_key.key;
        ssl_client_certificate /path/to/ssl_client_certificate.cer;

        error_page 497 301 =307 https://89.89.89.89:7000$request_uri;

        location / {
            proxy_pass http://89.89.89.89:3000/;

            proxy_pass_header Server;
            proxy_set_header Host $http_host;
            proxy_redirect off;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-Protocol $scheme;
        }
    }

但是,如果客户端通过除 GET 之外的任何其他方法发出请求,则该请求将转换为 GET。因此,为了保留客户端传入的请求方法;我们使用错误处理重定向,如下所示nginx 文档中的 error_page

这就是我们使用301 =307重定向的原因。

使用此处显示的 nginx.conf 文件,我们可以让 http 和 https 监听同一个端口

答案2

对于那些可能正在搜索的人:

ssl on;和添加error_page 497 $request_uri;到您的服务器定义。

答案3

从 1.15.2 开始终于可以正确执行此操作。请参阅信息这里

在您的 nginx.conf 中添加如下块(在 http 块之外):

stream {
    upstream http {
        server localhost:8000;
    }

    upstream https {
        server localhost:8001;
    }

    map $ssl_preread_protocol $upstream {
        default https;
        "" http;
    }

    server {
        listen 8080;
        listen [::]:8080;
        proxy_pass $upstream;
        ssl_preread on;
    }
}

然后,您可以创建常规服务器块,但监听以下不同的端口:

server {
    listen 8000;
    listen [::]:8000;
    listen 8001 ssl;
    listen [::]:8001 ssl;
...

这样,流块能够预读并检测它是否是 TLS(在此示例中为端口 8080),然后代理将其本地传递到正确的服务器端口。

答案4

是的,这是可能的,但需要修补 nginx 源代码(HoverHell 有无需修补的解决方案)。Nginx 将此视为错误配置,而不是有效配置。

变量 $ssl_session_id 可用于区分普通连接和 SSL 连接。

针对 nginx-0.7.65 的补丁:

--- src/http/ngx_http_request.c-orig    2011-05-03 15:47:09.000000000 +0200
+++ src/http/ngx_http_request.c 2011-05-03 15:44:01.000000000 +0200
@@ -1545,12 +1545,14 @@

    c = r->connection;

+    /* disable plain http over https port warning
     if (r->plain_http) {
         ngx_log_error(NGX_LOG_INFO, c->log, 0,
                       "client sent plain HTTP request to HTTPS port");
         ngx_http_finalize_request(r, NGX_HTTP_TO_HTTPS);
         return;
     }
+    */

#if (NGX_HTTP_SSL)

服务器配置:

server {
    listen 80;
    index index.html;

    location / {
        root html;
        if ($ssl_session_id) {
            root html_ssl;
        }
    }

    ssl on;
    ssl_certificate cert.crt;
    ssl_certificate_key cert.key;
}

相关内容