Laravel WebSockets + SPA (Laravel Echo + Passport)

At work, I need to add a chatting feature then need Websocket. First, I was thinking to use services like pusher, PubNub, etc… But, those are a bit expensive. Still okay to use for our usage, but I keep exploring the other alternatives and found out the Laravel WebSockets package and socket.io with Redis. After discussing, the conclusion was to use Laravel Websocket.

Some problems, but it’s okay

When I deploy the Laravel WebSockets, I got some problems that stuck to me for deploying. I am writing down those like a tutorial or notes for myself. Maybe, that might help your problem as well?

Notes: This is like a note for my own and this is not a tutorial. For the tutorials, I will list down in the following… Oh yah, please read the official docs first.

* Articles ✍️*
https://alex.bouma.dev/installing-laravel-websockets-on-forge
https://johnbraun.blog/posts/websockets-in-laravel
https://medium.com/@danielalvidrez/broadcasting-with-laravel-echo-vue-js-1dc0fb488e54
https://ma.ttias.be/deploying-laravel-websockets-with-nginx-reverse-proxy-and-supervisord/
https://ohdear.app/blog/transitioning-from-laravel-echo-server-to-laravel-websockets
https://freek.dev/1228-introducing-laravel-websockets-an-easy-to-use-websocket-server-implemented-in-php
https://medium.com/@marques.joaopereira/laravel-echo-laravel-echo-server-api-authentication-using-passport-1d6b847f91c6
https://medium.com/@mihkelallorg/laravel-react-jwt-tokens-and-laravel-echo-with-pusher-for-dummies-like-me-cafc5a09a1a1

* Videos 📹*
Self-Host Your Own Websockets with Laravel – It’s This Easy and Fast
Laravel Websockets – Package Usage & Demos
Laravel Websocket Package By BeyondCode and Spatie | Free Real Time System

Laravel Web Socket on Separate Server

My first issue was, I want to host Laravel Web Socket on a separate server. That one is pretty easy to do. I just install the Laravel Web Socket package to my application and host that on a separate server and use that from the application. It will be something like the following

The old stage it’s My Laravel App. Then, after My Laravel App (server 1) + Laravel Web Socket and My Laravel App + Laravel Web Socket (server 2). When I set up that, first I need to update config/websocket.php apps section. Like the following…

'apps' => [
        [
            'id' => env('PUSHER_APP_ID'),
            'name' => env('APP_NAME'),
            'key' => env('PUSHER_APP_KEY'),
            'secret' => env('PUSHER_APP_SECRET'),
            'path' => env('PUSHER_APP_PATH'),
            'capacity' => null,
            'enable_client_messages' => false,
            'enable_statistics' => true,
            'host' => env('LARAVEL_WEB_SOCKET_HOST', 'socket.example.com'),
        ],
    ],

I added host option. In our case, the value will be socket.example.com. After that, you can see the dashboard from example.com/laravel-websocket which connect socket server socket.example.com . Of course, you need to set up the rest of your environment as well. But, for those, I assume you got the idea from the documentation and other tutorials.

Setting up SSL to Laravel Web Socket

For this one, you can check examples from the documentation. For me, I use the proxy like the following…

# FORGE CONFIG (DO NOT REMOVE!)
include forge-conf/socket.example.com/before/*;

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name socket.example.com;
    root /home/forge/socket.example.com/public;

    # FORGE SSL (DO NOT REMOVE!)
    ssl_certificate /etc/nginx/ssl/socket.example.com/xxxxxx/server.crt;
    ssl_certificate_key /etc/nginx/ssl/socket.example.com/xxxxxx/server.key;

    ssl_protocols TLSv1.2;
    ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384;
    ssl_prefer_server_ciphers on;
    ssl_dhparam /etc/nginx/dhparams.pem;

    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-XSS-Protection "1; mode=block";
    add_header X-Content-Type-Options "nosniff";

    index index.html index.htm index.php;

    charset utf-8;

    # FORGE CONFIG (DO NOT REMOVE!)
    include forge-conf/socket.example.com/server/*;
    
    location / {
        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;
    }

    location = /favicon.ico { access_log off; log_not_found off; }
    location = /robots.txt  { access_log off; log_not_found off; }

    access_log off;
    error_log  /var/log/nginx/socket.example.com-error.log error;

    error_page 404 /index.php;

    location ~ \.php$ {
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass unix:/var/run/php/php7.3-fpm.sock;
        fastcgi_index index.php;
        include fastcgi_params;
    }

    location ~ /\.(?!well-known).* {
        deny all;
    }
}

# FORGE CONFIG (DO NOT REMOVE!)
include forge-conf/socket.example.com/after/*;

Yep, I assume. You are running Laravel Web Socket Server with supervisord. It was a bit confusing when I do the https setup and this article (Laravel Websockets on Forge) by Alex Bouma make me clear. Go read it, if you are confused. You should definitely check this old closed Github issue as well.

After you finish setting up https on WebSocket server, you should update your environment key of LARAVEL_WEBSOCKETS_PORT to 443 as well.

Now time to connect via frontend

In my case, the frontend is on a separate server and it was already using Laravel passport to authenticate. The setup will be something like the following…

import Echo from 'laravel-echo';

window.Pusher = require('pusher-js');

window.Echo = new Echo({
    broadcaster: 'pusher',
    key: 'same_key_as_your_socket_server_pusher_key',
    wsHost: 'socket.example.com',
    authEndpoint: 'https://example.com/api/broadcasting/auth',
    auth: {
        headers: {
           Authorization: "Bearer " + token
        }
    },
    wssPort: 443,
    disableStats: true,
    encrypted: true,
    enabledTransports: ['wss', 'ws']
});

Note:: wsHost and authEndpoint are different.

Yep, please don’t forget to update your BroadcastServiceProvider.

Broadcast::routes([
    'prefix' => 'api',
    'middleware' => 'auth:api'
]);

require base_path('routes/channels.php');

And there we go… Laravel + Laravel Web Socket + SPA + Passport.

Leave a Reply