我试图路线Nginx 中的请求方式如下:
- 向 发出的请求
/
应该转到 PHP 脚本(proxy.php
,它本身就是一个代理) - 请求
/websocket
应代理至http://localhost:4000/websocket
- 所有其他请求都应代理至
http://localhost:4000/
我可以得到2.和3.使用以下配置工作:
server {
listen 443 ssl;
server_name proxy.domain.com;
ssl_certificate /etc/nginx/ssl/proxy.domain.com/468446/server.crt;
ssl_certificate_key /etc/nginx/ssl/proxy.domain.com/468446/server.key;
location = /websocket/ {
proxy_pass http://127.0.0.1:4000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $http_host;
proxy_pass http://127.0.0.1:4000;
}
}
然后我尝试找到一种方法来添加1.并得出了以下结论:
server {
listen 443 ssl;
server_name proxy.domain.com;
root /var/www/dowmain;
ssl_certificate /etc/nginx/ssl/proxy.domain.com/468446/server.crt;
ssl_certificate_key /etc/nginx/ssl/proxy.domain.com/468446/server.key;
location = /websocket/ {
proxy_pass http://127.0.0.1:4000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $http_host;
proxy_pass http://127.0.0.1:4000;
}
location = / {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root/proxy.php;
fastcgi_pass unix:/var/run/php/php7.2-fpm.sock;
}
}
然而,在这种配置下,2.不再起作用。对 的请求/websocket
由 PHP 脚本处理。似乎/websocket
位置块不再起作用。
有趣的是,如果我将配置切换为http://
,一切都会正常工作。
知道我做错了什么吗?
更新
我认为我可以排除location /websocket { ... }
是问题的根源,因为如果我location = / { ... }
用块中的规则替换块中的 PHP 配置内容location / { ... }
,它会正常工作(但这不是我需要的)。所以我怀疑是 PHP 把一切都搞乱了。
更新二
它甚至可以在我的本地机器上使用来自的证书mkcert
,并且具有完全相同的配置。
所以唯一的区别是让我们加密证书与本地证书。甚至 Nginx 和 PHP 版本也基本相同(我本地机器上的 PHP 7.2.7、Nginx 1.15.1 与 PHP 7.2.13、Nginx 1.15.6)
答案1
似乎包含 proxy_pass 指令的位置末尾的斜杠有问题。您可以尝试将其添加到您的 websocket 位置:
location /websocket/ {
proxy_pass http://127.0.0.1:4000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
也因为这我从 proxy_pass 指令中删除了 /websocket/,因为您将 URI 的“/webconfig”部分替换为“/webconfig/”,这很令人困惑
如果 proxy_pass 指令指定了 URI,那么当请求传递给服务器时,规范化请求 URI 中与位置匹配的部分将被指令中指定的 URI 替换。
对于主要问题——基于Nginx 文档:
如果位置由以斜杠字符结尾的前缀字符串定义,并且请求由 proxy_pass、fastcgi_pass、uwsgi_pass、scgi_pass、memcached_pass 或 grpc_pass 之一处理,则将执行特殊处理。响应 URI 等于此字符串但没有尾部斜杠的请求,将返回带有代码 301 的永久重定向到附加了斜杠的请求 URI。
我认为您的情况下,websocket 位置没有尾随斜杠,并且根据此规则它被 301 重定向到 / 位置。
答案2
您的配置对我来说看起来不错,我在我的 nginx 设置中仔细检查了它,它做了它需要做的事情。我通过在不同位置添加标头进行了检查:
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name proxy.domain.com;
root /var/www/dowmain;
ssl_certificate /etc/nginx/ssl/proxy.domain.com/468446/server.crt;
ssl_certificate_key /etc/nginx/ssl/proxy.domain.com/468446/server.key;
ssl_protocols TLSv1.2;
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384;
ssl_prefer_server_ciphers on;
ssl_dhparam /etc/nginx/dhparams.pem;
location /websocket {
proxy_pass http://127.0.0.1:4000/websocket/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
add_header X-location websocket always;
}
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $http_host;
proxy_pass http://127.0.0.1:4000;
add_header X-location wildcard always;
}
location = / {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root/proxy.php;
fastcgi_pass unix:/var/run/php/php7.2-fpm.sock;
add_header X-location root always;
}
}
然后使用以下命令检查标题:
$ curl -I https://proxy.domain.com
$ curl -I https://proxy.domain.com/anythingelse
$ curl -I https://proxy.domain.com/websocket
输出应包含:
X-location: websocket
或者:
X-location: wildcard
或者:
X-location: root
X-location 的值应该与您在 nginx 配置中为该块设置的值相匹配。
我的 nginx 版本是:
# nginx -v
nginx version: nginx/1.10.1