Home > Software design >  Nginx WSS (port 443) to WS (port 80) rewrite rule containing regex URL
Nginx WSS (port 443) to WS (port 80) rewrite rule containing regex URL

Time:03-08

Apache

I have the following RewriteRule in the Apache web proxy server configuration:

<IfModule mod_ssl.c>
<VirtualHost *:443>
        ServerName myserver.example.com
        ServerAdmin [email protected]
 
        # Rewrite rule for the WebSocket connection
        RewriteEngine On
        RewriteCond %{REQUEST_URI}  ^/[0-9] /ws/graph/            [NC]
        RewriteRule /(.*)           ws://localhost:8000/$1 [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>

(Maybe it can be even simplified to RewriteRule ^/[0-9] /ws/graph/ ws://localhost:8000/$1 [P,L], I didn't check that.)

This configuration works fine and I successfully connect with the application behind the proxy server using HTTP and WebSockets, depending on which URL I use.


Nginx

Now, I am trying to recreate the same behavior using Nginx.

I tried:

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/$1;

       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;

}

and:

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/$1;

            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;

}

I also tried adding the following headers to the WebSocket's location:

       location ~ ^/[0-9] /ws/graph/ {
            proxy_pass http://localhost:8000/$1;
            
            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;
       }

and this rewrite rule clause:

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

For all above cases of the nginx configuration all requests are treated as HTTP requests in the application at localhost:8000/.

What I am trying to achieve is to get the following connection working, e.g., connecting from the browser using JavaScript:

const ws = new WebSocket(`wss://myserver.example.com/1234123/ws/graph/`);

I am not an expert on Nginx and the syntax for the rules is hardly documented, or I cannot find it. All suggestions are welcomed.


Sources

CodePudding user response:

A colleague helped me with this one. Below I post the answer.

The solution

The proper configuration is:

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;

}

What I did wrong

My mistake was that I tied:

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

and:

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

both of which will not work, with the former (note the "/" at the end of the URL line) resulting in an error:

    "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

Also note that a separate location must be there to handle "normal" HTTP requests, else the requests won't be proxied properly:

       ...

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

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

       ...
  • Related