Laravel-websockets: Websocket fails with 404 when trying to connect over SSL

Created on 17 Aug 2019  路  6Comments  路  Source: beyondcode/laravel-websockets

I'm trying to setup laravel-websockets using nginx as a reverse proxy. I can't seem to get past this 404 error and I'm not sure what's setup wrong. I've tried manually running php artisan websockets:serve and via supervisor. I can tell something's happening because when that is not running I get a 502 error. I've got this running locally without SSL, so I know my general config is good.

Here's the socket request url:
wss://myapp.com/ws/app/app_key?protocol=7&client=js&version=4.4.0&flash=false

.env

BROADCAST_DRIVER=laravel-websocket
LARAVEL_WEBSOCKETS_APP_ID=app_id
LARAVEL_WEBSOCKETS_APP_KEY=app_key
LARAVEL_WEBSOCKETS_APP_SECRET=secret_key
MIX_SOCKET_APP_KEY="${LARAVEL_WEBSOCKETS_APP_KEY}"
MIX_SOCKET_PATH=/ws

broadcasting.php

'laravel-websocket' => [
    'driver' => 'pusher',
    'key' => env('LARAVEL_WEBSOCKETS_APP_KEY'),
    'secret' => env('LARAVEL_WEBSOCKETS_APP_SECRET'),
    'app_id' => env('LARAVEL_WEBSOCKETS_APP_ID'),
    'options' => [
        'host' => '127.0.0.1',
        'port' => 6001,
        'scheme' => env('LARAVEL_WEBSOCKETS_SCHEME', 'http')
    ],
],

websockets.php (everything else is default)

'apps' => [
    [
        'id' => env('LARAVEL_WEBSOCKETS_APP_ID'),
        'name' => env('APP_NAME'),
        'key' => env('LARAVEL_WEBSOCKETS_APP_KEY'),
        'secret' => env('LARAVEL_WEBSOCKETS_APP_SECRET'),
        'capacity' => null,
        'enable_client_messages' => true,
        'enable_statistics' => true,
    ],
],

Client Config

let echoConfig = {
    broadcaster: 'pusher',
    key: process.env.MIX_SOCKET_APP_KEY,
    wsHost: window.location.hostname,
    wsPort: process.env.MIX_SOCKET_PORT || null,
    wsPath: process.env.MIX_SOCKET_PATH || null,
    disableStats: true,
    enabledTransports: ['ws', 'wss'],
    encrypted: true
}

nginx conf

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;

    ...

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    ...

    location /ws {
        proxy_pass             http://127.0.0.1:6001;
        proxy_set_header Host  $host;
        proxy_read_timeout     60;
        proxy_connect_timeout  60;
        proxy_redirect         off;

        # Allow the use of websockets
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }

    ssl_certificate /etc/letsencrypt/live/myapp.com/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/myapp.com/privkey.pem; # managed by Certbot
}

Client error:
Error during WebSocket handshake: Unexpected response code: 404

Nginx error:
connect() failed (111: Connection refused) while connecting to upstream,

Most helpful comment

I figured out the problem here, I was using location /ws in nginx and wsPath: '/ws' in the client. While this worked for proxying in nginx, the backend was not setup to handle the /ws route and I'm not sure how I would configure that, so of course it was returning a 404.

I followed the @ syntax in the nginx examples and now everything appears to be working.

map $http_upgrade $type {
  default "web";
  websocket "ws";
}

server {
  # Your default configuration comes here...

  location / {
    try_files /nonexistent @$type;
  }

  location @web {
    try_files $uri $uri/ /index.php?$query_string;
  }

  location @ws {
    proxy_pass             http://127.0.0.1:6001;
    proxy_set_header Host  $host;
    proxy_read_timeout     60;
    proxy_connect_timeout  60;
    proxy_redirect         off;

    # Allow the use of websockets
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection 'upgrade';
    proxy_set_header Host $host;
    proxy_cache_bypass $http_upgrade;
  }
}

All 6 comments

Are you using AWS? You might have enabled port 6001 on Forge, but it might be disabled by AWS security group. I am struggling with the same issue. Working fine on Homestead for me.

See
https://www.portcheckers.com/

@xlcrr I'm not using forge but I am on AWS. I wouldn't think that would be an issue since all websocket traffic is coming in through port 443 on the route /ws then nginx sends it to port 6001 internally.

I figured out the problem here, I was using location /ws in nginx and wsPath: '/ws' in the client. While this worked for proxying in nginx, the backend was not setup to handle the /ws route and I'm not sure how I would configure that, so of course it was returning a 404.

I followed the @ syntax in the nginx examples and now everything appears to be working.

map $http_upgrade $type {
  default "web";
  websocket "ws";
}

server {
  # Your default configuration comes here...

  location / {
    try_files /nonexistent @$type;
  }

  location @web {
    try_files $uri $uri/ /index.php?$query_string;
  }

  location @ws {
    proxy_pass             http://127.0.0.1:6001;
    proxy_set_header Host  $host;
    proxy_read_timeout     60;
    proxy_connect_timeout  60;
    proxy_redirect         off;

    # Allow the use of websockets
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection 'upgrade';
    proxy_set_header Host $host;
    proxy_cache_bypass $http_upgrade;
  }
}

@johnwilhite did you got it working?
I have a Elastic Beanstalk application, i open port 6001 and I can see the websocket request coming on EC2 the access.log, but I guess it's not forwarding to the right docker container port.

@fernandocoronatomf Yes it's working for me now. I did not open port 6001, since that is only used by the internal proxy. Nginx proxies websocket requests from port 443 to 6001 internally. Not sure your issue is the same as mine was, but hope that helps.

@johnwilhite how did you added "ws" as a prefix for the websocket route?
So I just got it working but a bit different setup.

My load balancer always sends the traffic to port 80, so I did that:

my conf.d

`upstream docker {
server 172.17.0.3:80;
keepalive 256;
}

upstream websocket {
server 172.17.0.3:6001;
keepalive 256;
}
`

My sites-enabled

` map $http_upgrade $connection_upgrade {
default "upgrade";
websocket "ws";
}

server {
    listen 80;

    gzip on;
    gzip_comp_level 4;
    gzip_types text/html text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;


    access_log    /var/log/nginx/access.log;


    location / {
            proxy_pass            http://docker;
            proxy_http_version    1.1;

        proxy_set_header    Connection            $connection_upgrade;
        proxy_set_header    Upgrade                $http_upgrade;
        proxy_set_header    Host                $host;
        proxy_set_header    X-Real-IP            $remote_addr;
        proxy_set_header    X-Forwarded-For        $proxy_add_x_forwarded_for;
    }

    location /app {
            proxy_pass             http://websocket;
            proxy_set_header Host  $host;
            proxy_read_timeout     60;
            proxy_connect_timeout  60;
            proxy_redirect         off;

            # Allow the use of websockets
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection 'upgrade';
            proxy_set_header Host $host;
            proxy_cache_bypass $http_upgrade;
    }
}

`

with this setup my websocket runs with no SSL because the load balancer always expects https

Was this page helpful?
0 / 5 - 0 ratings