Rails app with Apache/Passenger: WebSocket connection failed after moving to subdirectory

Environment

  • Rails application + Vue.js
  • Rocky Linux server
  • Apache with Passenger
  • Application served under subdirectory /myapp

Issue

WebSocket connection fails after updating the application URL path. The application is served through an Apache VirtualHost configuration with Passenger, and while all other application features (API, static assets) are working correctly, the WebSocket connection cannot be established.

Expected Behavior

WebSocket connection should establish successfully for real-time features through the /myapp/cable endpoint.

Current Behavior

WebSocket connection fails to establish. Browser console shows:

WebSocket connection to 'ws://domain/myapp/cable' failed: WebSocket is closed before the connection is established.

All requests to the WebSocket endpoint return ā€˜failedā€™ status in browser Network tab with the following request headers:

connection: Upgrade
upgrade: websocket
sec-websocket-protocol: actioncable-v1-json, actioncable-unsupported

Questions

  1. Is there a way to gather more detailed information about the source of the error?
  2. How can I properly configure the VirtualHost to handle WebSocket connections in a subdirectory?
  3. Are there common gotchas when setting up Action Cable with Passenger in a subdirectory?

Current VirtualHost configuration:

<VirtualHost *:80>
    ServerName xxx
    DocumentRoot /var/www/html/mainapp/public

    <Directory /var/www/html/mainapp>
        Allow from all
        Options -MultiViews
        Require all granted
    </Directory>

    <Location /myapp>
        PassengerBaseURI /myapp
        PassengerAppRoot /var/www/html/myapp
        PassengerEnabled on
        Require all granted
    </Location>

    <Directory /var/www/html/myapp/public>
        Allow from all
        Options -MultiViews
        Require all granted
        AllowOverride All
    </Directory>
</VirtualHost>

Any help or guidance would be appreciated!

Update I found this issue: GitHub - Phusion Passenger #1202.

It seems that WebSockets have difficulties working with Apache. For now, Iā€™ve decided to give up on setting up WebSockets.

However, Iā€™m facing another issue: I canā€™t access my app properly. Passenger is running, but when I go to my appā€™s URL, it displays the appā€™s files instead of running the application.

I updated my VirtualHost configuration since this is now a production server with multiple applications already configured:

<VirtualHost *:80>
    ServerName xxx
    DocumentRoot /var/www/html/public

    <Directory /var/www/html/myapp/public>
        Allow from all
        Options -MultiViews
        Require all granted
        AllowOverride All
        PassengerEnabled on
    </Directory>

    PassengerBaseURI /myapp
    PassengerAppRoot /var/www/html/myapp

</VirtualHost>

And i canā€™t find any logs from apache or my app giving me detailed informations about this.

Does anyone have an idea why Passenger is not serving the app correctly? Any help would be appreciated!

Your WebSocket issue is likely due to Passenger and Apache not forwarding WebSocket connections properly. Try these steps:

Enable WebSocket support in Apache by adding: RewriteEngine On RewriteCond %{HTTP:Upgrade} =websocket [NC] RewriteRule /(.*) ws://localhost:3000/$1 [P,L]

Ensure Passenger supports WebSockets by adding

PassengerAppRoot /var/www/html/myapp PassengerAppType rack PassengerStartupFile config.ru

Check ActionCable settings in config/environments/production.rb config.action_cable.url = ā€˜ws://domain/myapp/cableā€™ config.action_cable.allowed_request_origins = [ā€˜http://domainā€™]

Use Passengerā€™s SetEnv for WebSockets:

SetEnvIf Request_URI ā€œ^/myapp/cableā€ passenger_enabled on

1 Like

Thanks for your reply ! Iā€™ll try it out ASAP and give you feedback when itā€™s done.

Hello Richard,

Sorry for the late reply, Iā€™ve been quite busy.

I tried your solution but couldnā€™t get it to workā€”Iā€™m not sure why. I suspect there was some misconfiguration in my project.

In the end, I reverted to a previous version and deployed using Docker. It turned out to be a lot easier, almost plug-and-play.

Hereā€™s my Docker configuration for WS :

<IfModule mod_ssl.c>
<VirtualHost *:443>
  ServerName xxx

  ProxyPreserveHost On
  ProxyRequests Off
  SSLEngine on

  ProxyPass "/" "http://localhost:8080/"
  ProxyPassReverse "/" "http://localhost:8080/"

  <Location /cable>
    ProxyPass "ws://localhost:8080/cable"
    ProxyPassReverse "ws://localhost:8080/cable"
  </Location>

  <Location />
    Require all granted
  </Location>

</VirtualHost>
</IfModule>

Thanks again for your helpā€”it really helped me understand how it works!