Search code examples
apachewebsocketshinyreverse-proxyshinyproxy

Apache Reverse Proxy and ShinyProxy


I wrote a shiny web application and deploy it on a server using ShinyProxy. Accessing the app directly via the IP address and port 8080 works fine. However, I need to connect it to a URL. On the ShinyProxy website there is an explanation on how it works with Nginx:

server {
  listen                80;
  server_name           shinyproxy.yourdomain.com;
  rewrite     ^(.*)     https://$server_name$1 permanent;
}

server {
  listen                443;
  server_name           shinyproxy.yourdomain.com;
  access_log            /var/log/nginx/shinyproxy.access.log;
  error_log             /var/log/nginx/shinyproxy.error.log error;

  ssl on;
  ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

  ssl_certificate       /etc/ssl/certs/yourdomain.com.crt;
  ssl_certificate_key   /etc/ssl/private/yourdomain.com.key;

   location / {
       proxy_pass          http://127.0.0.1:8080/;

       proxy_http_version 1.1;
       proxy_set_header Upgrade $http_upgrade;
       proxy_set_header Connection "upgrade";
       proxy_read_timeout 600s;

       proxy_redirect    off;
       proxy_set_header  Host             $http_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;
     }

}

Unfortunately, I need to use Apache, i.e. Apache/2.4.43 (Debian). I tried various configurations, but I do not get it to work. Simply connecting the target URL to the port on the server allows me to load the app in the first place. Though after loading the app, the screen immediately turns grey and the app unresponsive. That happens because simply linking a URL to an IP address does not correctly account for the use of web sockets.

Does anyone know what the correct Apache file should look like? How do I connect the app, which does not require user autentication, to a URL (e.g. the above mentioned shinyproxy.yourdomain.com)?


Solution

  • If you have a unique URL for the websocket endpoint, just load mod_proxy_wstunnel and target that traffic first. In the example below /Silly/ws is the websocket endpoint:

    ProxyPassMatch ^/(Silly/ws)$ ws://localhost:9080/$1
    ProxyPass / http://localhost:9080/
    

    Current releases of Apache do not handle the case very well where a single URL is used for both non-upgraded and upgraded traffic. If you have this situation, you can use a snippet like this conditionally do websockets on any of the proxied URLs:

    ProxyPass / http://localhost:9080/
    RewriteEngine on
    RewriteCond %{HTTP:Upgrade} websocket [NC]
    RewriteCond %{HTTP:Connection} upgrade [NC]
    RewriteRule ^/?(.*) "ws://localhost:9080/$1" [P,L]
    

    A future 2.4.x release is likely to support a simple scenario like current httpd trunk:

    ProxyPass / ws:/localhost:9080/
    ProxyPass / http://localhost:9080/