我想知道 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;
}