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
- https://stackoverflow.com/a/16159322/8877692
- https://www.nginx.com/blog/websocket-nginx/
- https://www.nginx.com/blog/converting-apache-to-nginx-rewrite-rules/
- https://www.digitalocean.com/community/tutorials/understanding-nginx-server-and-location-block-selection-algorithms#location-block-syntax
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/;
}
...