WebSockets are an essential part of many modern web applications. With WebSockets, you can create applications that update in real-time without the need to reload the page or continually poll the server for changes.
Laravel supports two server-side broadcasting drivers out of the box: Pusher and Ably. While these two solutions may be easier to set up and can provide additional functionalities, they are both commercial solutions. This makes them unideal if you’re on a tight budget or if you’re simply looking for an open source solution.
Laravel WebSockets is a great open source alternative. It allows you to easily add WebSocket support to your Laravel >5.7 application. It comes with a debug dashboard and real-time statistics, among other features.
In this tutorial, we are going to discuss how to set up Laravel WebSockets on a subdomain. Specifically, we’ll go over the following topics:
laravel-websockets
Before we begin learning how to set up and use Laravel WebSockets on a subdomain, let’s go over some background information on the relationship between Laravel WebSockets and Pusher to prevent any confusion.
When using Laravel WebSockets, you are supposed to set Laravel’s BROADCAST_DRIVER
configuration to pusher
. You should also set other pusher
configurations, like PUSHER_APP_ID
, PUSHER_APP_KEY
, and PUSHER_APP_SECRET
, as well as install the pusher/pusher-php-server
composer package.
This makes the package seem like a Pusher software development kit (SDK) that requires Pusher credentials to work. But this is not the case. You actually don’t need Pusher credentials to use Laravel WebSockets!
The main idea behind Laravel WebSockets is to replace the Pusher driver without letting the Laravel framework know about this replacement.
When you are using Laravel WebSockets, Laravel thinks you are using the Pusher driver and will enable its functionalities. Under the hood, however, these functionalities have been replaced by the Laravel WebSockets package.
Most importantly, the package replaces calls to the Pusher server with calls to our own server, where the package then handles the requests. It also implements the Pusher message protocol. As a result, all existing packages and applications that support Pusher work with the package as well.
If you have a standard Laravel setup where both the frontend and backend are by Laravel in one root folder, you may have less trouble setting up this package. Going through the package documentation should get you up and running in no time.
If your application’s frontend is completely separated from the backend, you’ll have to set things up a little differently. This kind of setup typically requires two separate repositories or at least two separate folders. Take awesomeapp-backend
and awesomeapp-frontend
for example.
You then need to set up two subdomains to point to each folder, like this:
awesomeapp.test
or app.awesomeapp.test
could point to your frontend folder called awesomeapp-frontend
api.awesome.test
could point to your backend folder called awesomeapp-backend
When you have a setup like this, one thing you will most likely have to deal with is CORS.
A webpage may freely load images, stylesheets, scripts, iframes, and videos from a different origin. However, certain cross-domain requests, such as AJAX requests, are forbidden by default by the same-origin security restriction implemented by all major web browsers.
Cross-Origin Resource Sharing (CORS) defines a way to specify exceptions to this restriction. It is a mechanism in which a browser and server can interact to determine whether it is safe to allow a cross-origin request.
So what does this mean for us? Well, our subdomains app.awesomeapp.test
and api.awesome.test
count as different origins, despite having the same domain host.
So, when we set up laravel-echo
in our frontend subdomain, app.awesomeapp.test
, the library will try to make an AJAX request to the backend subdomain, api.awesomeapp.test
. Because they will be seen as separate origins by the browser, the request will fail…
…unless we enable CORS. Thankfully, enabling CORS in Laravel is pretty easy.
To configure CORS for our Laravel WebSocket server, open the cors.php
configuration file from the config
folder of your Laravel installation. Add Laravel’s broadcasting/auth
route to the paths
array and set the supports_credentials
option to true
.
Under the hood, the supports_credentials
option sets your application’s [Access-Control-Allow-Credentials]
header to a value of true
:
// config/cors.php return [ 'paths' => ['api/*', 'sanctum/csrf-cookie', 'broadcasting/auth'], // ... 'supports_credentials' => true, ];
Alternatively, you may place the Broadcast::routes
method call within your routes/api.php
file. This prefixes the broadcasting/auth
route with api/
, removing the need to add the route to the paths
array since the api/*
path (every route with api
prefix) is in the array by default.
When calling the Broadcast::routes
method, you should specify the middleware to be used when making requests to the channel routes. This could be auth:api
or auth:sanctum
if you are using Laravel Sanctum.
// routes/api.php Broadcast::routes(['middleware' => ['auth:sanctum']]);
// config/cors.php return [ 'paths' => ['api/*', 'sanctum/csrf-cookie'], // ... 'supports_credentials' => true, ];
Also, make sure that HTTP/AJAX requests from your frontend have the XMLHttpRequest.withCredentials
option set to true
. If you are using axios
, this can be done by setting the withCredentials
option on your application’s global axios
instance like in the code below:
axios.defaults.withCredentials = true;
Finally, you should ensure your application’s session cookie is accessible from any subdomain of your root domain. This can be done by prefixing the domain with a leading .
within your application’s config/session.php
configuration file.
'domain' => '.example.com',
To make the configuration environmental agonistic, you should instead set this option using the SESSION_DOMAIN
environmental variable within your .env
file:
// config/session.php 'domain' => env('SESSION_DOMAIN', null),
# .env # ... SESSION_DOMAIN=.example.com
Now, let’s go ahead and set up Laravel WebSockets!
laravel-websockets
Laravel WebSockets can be installed using composer.
composer require beyondcode/laravel-websockets
The package comes with a migration to store statistical information about your WebSocket server. You can skip this step if you do not plan on using this feature.
Run the following command to publish the migration file:
php artisan vendor:publish --provider="BeyondCode\LaravelWebSockets\WebSocketsServiceProvider" --tag="migrations"
Then, run the migrations:
php artisan migrate
Next, publish the Laravel WebSocket configuration file by running the following command:
php artisan vendor:publish --provider="BeyondCode\LaravelWebSockets\WebSocketsServiceProvider" --tag="config"
As we discussed, the Laravel WebSockets package works in combination with Pusher, so we also need to install the official Pusher PHP SDK:
composer require pusher/pusher-php-server "~3.0"
Make sure to use Pusher as your broadcasting driver. This can be done by setting the BROADCAST_DRIVER
environment variable within your .env
file:
BROADCAST_DRIVER=pusher
Lastly, make sure that the APP_NAME
, PUSHER_APP_ID
, PUSHER_APP_KEY
, and PUSHER_APP_SECRET
environmental variables are set in your .env
file.
N.B., It does not matter what you set as your PUSHER_
variables, just make sure they are unique for each project.
PUSHER_APP_ID=pusherid PUSHER_APP_KEY=pusherkey PUSHER_APP_SECRET=pushersecret PUSHER_APP_CLUSTER=pushercluster
As mentioned earlier, when broadcasting events from your Laravel application, the default behavior of the Pusher driver is to send the event information to the official Pusher server. But since the Laravel WebSockets package comes with its own Pusher API implementation, we need to tell Laravel to send the event information to our own server.
We do this by adding the host
and port
configuration key to the pusher
section of our config/broadcasting.php
file. The default port of the Laravel WebSocket server is 6001
.
'pusher' => [ 'driver' => 'pusher', 'key' => env('PUSHER_APP_KEY'), 'secret' => env('PUSHER_APP_SECRET'), 'app_id' => env('PUSHER_APP_ID'), 'options' => [ 'cluster' => env('PUSHER_APP_CLUSTER'), 'encrypted' => true, 'host' => '127.0.0.1', 'port' => 6001, 'scheme' => 'http' ], ],
Laravel WebSockets uses the concept of apps
to support multitenancy out of the box. This allows you to host the package separately from your current Laravel application and serve multiple WebSocket applications with one server.
The default app uses your existing Pusher configuration. This should do for most use cases.
'apps' => [ [ 'id' => env('PUSHER_APP_ID'), 'name' => env('APP_NAME'), 'key' => env('PUSHER_APP_KEY'), 'secret' => env('PUSHER_APP_SECRET'), 'enable_client_messages' => false, 'enable_statistics' => true, ], ],
Be sure to use the same id
, key
, and secret
from your broadcasting configuration section, otherwise broadcasting events from Laravel will not work.
Usually, all WebSocket messages go through your Laravel application before being broadcast to other users. But sometimes you may want to send an event directly from one client to another without it going to the server.
For example, take a typing event in a chat application. You can configure this option for each app in your config/websockets.php
configuration file using the enable_client_messages
key.
You should use this feature with care since these event messages originate from other users and could be subject to tampering.
As mentioned earlier, the Laravel WebSockets package comes with a dashboard to monitor statistical information about your WebSocket server. To enable or disable this feature for any of your apps, you can modify the enable_statistics
option.
To enable event broadcasting in Laravel, you need to register the App\Providers\BroadcastServiceProvider
. You can do this by adding App\Providers\BroadcastServiceProvider::class
to the providers
array of your config/app.php
file:
'providers' => [ // ... App\Providers\BroadcastServiceProvider::class, // ... ],
In new Laravel applications, this provider is already in the array, you just need to uncomment it.
The BroadcastServiceProvider
contains the code necessary to register the broadcast authorization routes and callbacks.
If you added the Broadcast::routes
method within the routes/api.php
file earlier, you can safely comment or remove this line in the boot
method of the BroadcastServiceProvider
.
public function boot() { // Broadcast::routes(); require base_path('routes/channels.php'); }
That’s all for the backend, now let’s set up the frontend app!
Laravel Echo is a JavaScript library that makes it very easy to subscribe to channels and listen for events broadcasted by your Laravel server-side broadcasting driver. In this case, it’s Laravel Websockets’ Pusher API implementation.
Echo can easily be installed via the npm package manager. We will also install pusher-js
since the Laravel Websockets package uses the Pusher Channels broadcaster:
npm install --save-dev laravel-echo pusher-js
To make Laravel Echo work with Laravel WebSockets, we need to pay attention to some configuration options when initializing Laravel Echo. Specifically, we need to add the wsHost
and wsPort
parameters and point them to our Laravel WebSocket server host and port.
In order for Laravel WebSocket authorization requests to succeed, we also need to provide a custom authentication endpoint using authEndpoint
and an authorization header using auth.headers.Authorization
.
When the authEndpoint
option is omitted, Laravel Echo will try to authenticate using the same host that it runs on. But in our case, since the WebSocket server is on a different subdomain, the request will fail.
N.B., If your Broadcast::routes
method call is in the App\Providers\BroadcastServiceProvider
file, which is the default, the value of your authEndpoint
should look something like: http://api.awesomeapp.test/broadcasting/auth
.
If you placed the method call within the routes/api.php
file, then it should be something like: http://api.awesomeapp.test/api/broadcasting/auth
.
import Echo from "laravel-echo" window.Pusher = require('pusher-js'); window.Echo = new Echo({ broadcaster: 'pusher', key: 'pusherkey', wsHost: 'api.websockets.test', wsPort: 6001, // wssPort: 6001, forceTLS: false, // encrypted: true, disableStats: true, auth: { headers: { Authorization: 'Bearer sometoken' } }, authEndpoint: 'http://api.awesomeapp.test/api/broadcasting/auth', // OR // authEndpoint: 'http://api.awesomeapp.test/broadcasting/auth', })
Alternatively, you can configure a custom authorizer when initializing Laravel Echo. This allows you to configure Laravel Echo to use your global axios
instance, which should be properly configured for cross-domain requests and with the right authorization headers.
import Echo from "laravel-echo" window.Pusher = require('pusher-js'); window.Echo = new Echo({ broadcaster: 'pusher', key: 'pusherkey', wsHost: 'api.awesomeapp.test', wsPort: 6001, // wssPort: 6001, // For SSL forceTLS: false, // encrypted: true, // For SSL disableStats: true, authorizer: (channel, options) => { return { authorize: (socketId, callback) => { axios .post('/api/broadcasting/auth', { socket_id: socketId, channel_name: channel.name, }) .then((response) => { callback(false, response.data) }) .catch((error) => { callback(true, error) }) }, } }, })
Make sure that the value of key
is the same as that of the PUSHER_APP_KEY
in your backend configuration.
By default, the Pusher JavaScript client tries to send statistic information, but we can disable this using the disableStats
option.
When using Laravel WebSockets in combination with a custom SSL certificate, be sure to use the wssPort
option instead of wsPort
. Also, use the encrypted
option and set it to true
.
And that’s it! Now you can use Laravel Echo features in combination with Laravel WebSockets.
In this article, we learned all about Laravel WebSockets. We discussed the relationship between the package and Pusher, how to handle CORS when working with multiple subdomains, and how to set up Laravel WebSockets in our application. We also learned how to set up Laravel Echo to work with Laravel WebSockets, especially when working with multiple subdomains.
While the main purpose of the laravel-websockets
package is to make the Pusher JavaScript client or Laravel Echo as easy to use as possible, you are not limited to this use case or the Pusher protocol.
To learn more about how you might use the package in other use cases, check out the Laravel WebSockets documentation.
Install LogRocket via npm or script tag. LogRocket.init()
must be called client-side, not
server-side
$ npm i --save logrocket // Code: import LogRocket from 'logrocket'; LogRocket.init('app/id');
// Add to your HTML: <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script> <script>window.LogRocket && window.LogRocket.init('app/id');</script>
Hey there, want to help make our blog better?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowLearn how to manage memory leaks in Rust, avoid unsafe behavior, and use tools like weak references to ensure efficient programs.
Bypass anti-bot measures in Node.js with curl-impersonate. Learn how it mimics browsers to overcome bot detection for web scraping.
Handle frontend data discrepancies with eventual consistency using WebSockets, Docker Compose, and practical code examples.
Efficient initializing is crucial to smooth-running websites. One way to optimize that process is through lazy initialization in Rust 1.80.