Nginx WSS(端口 443)到 WS(端口 80)重写规则包含正则表达式 URL

Nginx WSS (port 443) to WS (port 80) rewrite rule containing regex URL

阿帕奇

我在 Apache 网络代理服务器配置中有以下 RewriteRule

<IfModule mod_ssl.c>
<VirtualHost *:443>
        ServerName myserver.example.com
        ServerAdmin admin@myserver.example.com
 
        # Rewrite rule for the WebSocket connection
        RewriteEngine On
        RewriteCond %{REQUEST_URI}  ^/[0-9]+/ws/graph/            [NC]
        RewriteRule /(.*)           ws://localhost:8000/ [P,L]
 
        # Logs for connections at port 443 (HTTPS/WSS)
        ErrorLog ${APACHE_LOG_DIR}/myserver.example.com-SSL-error.log
        CustomLog ${APACHE_LOG_DIR}/myserver.example.com-SSL-access.log combined
 
        # HTTPS => HTTP redirect for requests to the application
        ProxyPass / http://localhost:8000/
        ProxyPassReverse / http://localhost:8000/
 
        # Alias and certificate configuration
        ServerAlias myserver.example.com
        SSLCertificateFile /etc/letsencrypt/live/myserver.example.com/fullchain.pem
        SSLCertificateKeyFile /etc/letsencrypt/live/myserver.example.com/privkey.pem
        Include /etc/letsencrypt/options-ssl-apache.conf
</VirtualHost>
</IfModule>

(也许还可以简化为RewriteRule ^/[0-9]+/ws/graph/ ws://localhost:8000/ [P,L],我没查过)

此配置工作正常,我使用 HTTP 和 WebSockets 成功连接到代理服务器后面的应用程序,具体取决于我使用的URL。


Nginx

现在,我正在尝试使用 Nginx 重新创建相同的行为。

我试过了:

server {

       server_name myserver.example.com;

       access_log /var/log/nginx/myserver.example.com_SSL-access.log;
       error_log  /var/log/nginx/myserver.example.com_SSL-error.log;

       rewrite ^/[0-9]+/ws/graph/ ws://localhost:8000/;

       location / {
           proxy_pass http://localhost:8000/;
       }


       listen [::]:443 ssl;
       listen 443 ssl;
       ssl_certificate /etc/letsencrypt/live/myserver.example.com/fullchain.pem;
       ssl_certificate_key /etc/letsencrypt/live/myserver.example.com/privkey.pem;
       include /etc/letsencrypt/options-ssl-nginx.conf;
       ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

}

和:

server {

       server_name myserver.example.com;

       access_log /var/log/nginx/myserver.example.com_SSL-access.log;
       error_log  /var/log/nginx/myserver.example.com_SSL-error.log;

       location ~ ^/[0-9]+/ws/graph/ {
            proxy_pass http://localhost:8000/;

            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "Upgrade";
            proxy_set_header Host $host;
       }

       location / {
           proxy_pass http://localhost:8000/;
       }


       listen [::]:443 ssl;
       listen 443 ssl;
       ssl_certificate /etc/letsencrypt/live/myserver.example.com/fullchain.pem;
       ssl_certificate_key /etc/letsencrypt/live/myserver.example.com/privkey.pem;
       include /etc/letsencrypt/options-ssl-nginx.conf;
       ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

}

我还尝试将以下 headers 添加到 WebSocket 的 location:

       location ~ ^/[0-9]+/ws/graph/ {
            proxy_pass http://localhost:8000/;
            
            proxy_set_header        Host $host;
            proxy_set_header        X-Real-IP $remote_addr;
            proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header        X-Forwarded-Proto $scheme;

            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "Upgrade";
            proxy_set_header Host $host;
       }

和这个重写规则子句:

      if ($request_uri ~ ^/[0-9]+/ws/graph/) {       
           rewrite (.*)  ws://localhost:8000/;
       }

对于 nginx 配置的所有上述情况,所有请求在 localhost:8000/ 的应用程序中都被视为 HTTP 请求。

我想要实现的是让以下连接正常工作,例如,使用 JavaScript:

从浏览器连接
const ws = new WebSocket(`wss://myserver.example.com/1234123/ws/graph/`);

我不是 Nginx 专家,规则的语法几乎没有记录,或者我找不到。欢迎所有建议。


来源

一位同事帮我解决了这个问题。下面是我post的答案。

解决方案

正确的配置是:

server {

       server_name myserver.example.com;

       access_log /var/log/nginx/myserver.example.com_SSL-access.log;
       error_log  /var/log/nginx/myserver.example.com_SSL-error.log;

       # WebSocket support
       location ~ ^/[0-9]+/ws/graph/ {
           proxy_pass http://localhost:8000;
           proxy_http_version 1.1;
           proxy_set_header Upgrade $http_upgrade;
           proxy_set_header Connection "Upgrade";
           proxy_set_header Host $host;
       }

       # HTTP proxy
       location / {
           proxy_pass http://localhost:8000/;
       }


       listen [::]:443 ssl;
       listen 443 ssl;
       ssl_certificate /etc/letsencrypt/live/myserver.example.com/fullchain.pem;
       ssl_certificate_key /etc/letsencrypt/live/myserver.example.com/privkey.pem;
       include /etc/letsencrypt/options-ssl-nginx.conf;
       ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

}

我做错了什么

我的错误是我打平了:

       location ~ ^/[0-9]+/ws/graph/ {
           proxy_pass http://localhost:8000/;
           ...
       }

和:

       location ~ ^/[0-9]+/ws/graph/ {
           proxy_pass http://localhost:8000/;
           ...
       }

两者都不起作用,前者(注意 URL 行末尾的 "/")导致错误:

    "proxy_pass" cannot have URI part in location given by regular expression, or inside named location, or inside "if" statement, or inside "limit_except" block in /etc/nginx/sites-enabled/vhost-myserver.example.com.conf:10

另请注意,必须有一个单独的 location 来处理“正常”HTTP 请求,否则请求将无法被正确代理:

       ...

       # WebSocket support
       location ~ ^/[0-9]+/ws/graph/ {
           proxy_pass http://localhost:8000;
           ...
       }

       # HTTP proxy
       location / {
           proxy_pass http://localhost:8000/;
       }

       ...