Laravel Reverb: Private &amp; Presence Channel Auth at Scale | Mohamed Said        [  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MH.png)   Mohamed Said Laravel Backend Engineer  ](https://msaied.com) [ Home ](https://msaied.com) [ Projects ](https://msaied.com/projects) [ Articles  ](https://msaied.com/articles) [ Certificates ](https://msaied.com/certificates) [ Contact ](https://msaied.com#contact-section) 

       [  ](https://github.com/EG-Mohamed)       

 [ Home ](https://msaied.com) [ Projects ](https://msaied.com/projects) [ Articles ](https://msaied.com/articles) [ Certificates ](https://msaied.com/certificates) [ Contact ](https://msaied.com#contact-section) 

  [ home ](https://msaied.com)    [ articles ](https://msaied.com/articles)    Laravel Broadcasting with Reverb: Per-Channel Authorization and Presence Channels at Scale        On this page       1. [  Why Channel Authorization Deserves More Attention ](#why-channel-authorization-deserves-more-attention)
2. [  Structuring Channel Routes for Real Authorization ](#structuring-channel-routes-for-real-authorization)
3. [  Presence Channels: The join Return Value Matters ](#presence-channels-the-codejoincode-return-value-matters)
4. [  Scaling Presence Sets ](#scaling-presence-sets)
5. [  Broadcasting Events Only to Authorized Channels ](#broadcasting-events-only-to-authorized-channels)
6. [  Key Takeaways ](#key-takeaways)

  ![Laravel Broadcasting with Reverb: Per-Channel Authorization and Presence Channels at Scale](https://cdn.msaied.com/208/b654521431b6021da07c8209170ed9e9.png)

  #laravel   #reverb   #broadcasting   #websockets   #real-time  

 Laravel Broadcasting with Reverb: Per-Channel Authorization and Presence Channels at Scale 
============================================================================================

     16 Jun 2026      3 min read    ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said  

       Table of contents

1. [  01   Why Channel Authorization Deserves More Attention  ](#why-channel-authorization-deserves-more-attention)
2. [  02   Structuring Channel Routes for Real Authorization  ](#structuring-channel-routes-for-real-authorization)
3. [  03   Presence Channels: The join Return Value Matters  ](#presence-channels-the-codejoincode-return-value-matters)
4. [  04   Scaling Presence Sets  ](#scaling-presence-sets)
5. [  05   Broadcasting Events Only to Authorized Channels  ](#broadcasting-events-only-to-authorized-channels)
6. [  06   Key Takeaways  ](#key-takeaways)

 Why Channel Authorization Deserves More Attention
-------------------------------------------------

Most tutorials stop at `Auth::check()` inside a channel route. In production, that is rarely enough. A multi-tenant SaaS, a collaborative document editor, or a live support dashboard each needs per-resource authorization that mirrors the same rules enforced in your HTTP layer — not a weaker copy of them.

Laravel Reverb is a first-party WebSocket server, but the authorization model is still driven by `routes/channels.php` and the `BroadcastServiceProvider`. Getting that layer right is the real work.

Structuring Channel Routes for Real Authorization
-------------------------------------------------

Avoid anonymous closures for anything non-trivial. Register a dedicated channel class instead:

```bash
php artisan make:channel OrderChannel

```

```php
// routes/channels.php
Broadcast::channel('orders.{orderId}', OrderChannel::class);

```

```php
// app/Broadcasting/OrderChannel.php
final class OrderChannel
{
    public function join(User $user, int $orderId): bool|array
    {
        $order = Order::findOrFail($orderId);

        // Reuse the same policy you use in controllers.
        return $user->can('view', $order);
    }
}

```

Using `$user->can()` delegates to your existing `OrderPolicy::view()` method. One rule, two enforcement points — no drift.

Presence Channels: The `join` Return Value Matters
--------------------------------------------------

For presence channels, returning `true` is not enough. You must return an array; that array becomes the member metadata broadcast to all subscribers.

```php
public function join(User $user, string $roomId): bool|array
{
    $room = ChatRoom::findOrFail($roomId);

    if (! $user->can('join', $room)) {
        return false;
    }

    return [
        'id'     => $user->id,
        'name'   => $user->display_name,
        'avatar' => $user->avatar_url,
    ];
}

```

Keep this payload small. Every subscriber receives it on `pusher:member_added`. Sending eager-loaded relationships here is a common mistake that bloats payloads and slows join acknowledgement.

Scaling Presence Sets
---------------------

Reverb stores presence membership in Redis by default when you configure the `reverb` driver with a Redis connection. The key concern at scale is **join/leave storms** — a deploy or network blip causes hundreds of clients to reconnect simultaneously.

Mitigate this with exponential back-off on the client side (Laravel Echo supports this via the `authEndpoint` retry config) and by keeping your auth endpoint fast:

```php
// config/broadcasting.php — tune the Reverb connection pool
'reverb' => [
    'driver' => 'reverb',
    'key'    => env('REVERB_APP_KEY'),
    'secret' => env('REVERB_APP_SECRET'),
    'app_id' => env('REVERB_APP_ID'),
    'options' => [
        'host'   => env('REVERB_HOST', '0.0.0.0'),
        'port'   => env('REVERB_PORT', 8080),
        'scheme' => env('REVERB_SCHEME', 'http'),
    ],
],

```

Cache the authorization result for short-lived presence joins (5–10 seconds is safe) using a tagged cache keyed on `user:{id}:channel:{name}`:

```php
public function join(User $user, string $roomId): bool|array
{
    return Cache::tags(['channel-auth'])
        ->remember("user:{$user->id}:room:{$roomId}", 8, function () use ($user, $roomId) {
            $room = ChatRoom::findOrFail($roomId);
            if (! $user->can('join', $room)) {
                return false;
            }
            return ['id' => $user->id, 'name' => $user->display_name];
        });
}

```

Invalidate the tag when room membership rules change (e.g., a user is removed from a room).

Broadcasting Events Only to Authorized Channels
-----------------------------------------------

Use `broadcastOn` to return a typed channel, not a raw string:

```php
final class OrderStatusUpdated implements ShouldBroadcast
{
    public function __construct(private readonly Order $order) {}

    public function broadcastOn(): array
    {
        return [new PrivateChannel("orders.{$this->order->id}")];
    }

    public function broadcastWith(): array
    {
        return ['status' => $this->order->status->value];
    }
}

```

Using `PrivateChannel` (or `PresenceChannel`) ensures Reverb enforces the auth handshake before delivering the event.

Key Takeaways
-------------

- **Delegate to policies**: reuse `Gate`/`Policy` inside channel classes — never duplicate authorization logic.
- **Return arrays from presence `join`**: returning `true` silently breaks presence membership metadata.
- **Keep join payloads minimal**: large arrays on `pusher:member_added` degrade performance for all subscribers.
- **Cache short-lived auth results**: reduces DB pressure during reconnect storms without meaningful security trade-offs.
- **Use typed channel classes**: `PrivateChannel` and `PresenceChannel` make intent explicit and prevent accidental public exposure.

 Found this useful?

          [  ](https://twitter.com/intent/tweet?url=https%3A%2F%2Fmsaied.com%2Farticles%2Flaravel-broadcasting-with-reverb-per-channel-authorization-and-presence-channels-at-scale&text=Laravel+Broadcasting+with+Reverb%3A+Per-Channel+Authorization+and+Presence+Channels+at+Scale) [  ](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fmsaied.com%2Farticles%2Flaravel-broadcasting-with-reverb-per-channel-authorization-and-presence-channels-at-scale) 

 Frequently Asked Questions 
----------------------------

  3 questions  

     Q01  Can I reuse Laravel policies inside channel authorization classes?        Yes. Call `$user-&gt;can('action', $model)` inside your channel's `join` method. It delegates to the same Gate and Policy you use in controllers, keeping authorization logic in one place. 

      Q02  What happens if a presence channel's `join` method returns `true` instead of an array?        Returning `true` grants access but sends no member metadata. Other subscribers won't receive meaningful data in `pusher:member_added` events, breaking any UI that displays who is online. 

      Q03  How do I handle reconnect storms in Reverb with many presence channel subscribers?        Cache the authorization result for a few seconds per user/channel pair using a tagged cache. This reduces database load during mass reconnects without weakening security, since the window is too short to be exploitable in practice. 

  Continue reading

 More Articles 
---------------

 [ View all    ](https://msaied.com/articles) 

 [ ![Laravel Pennant: Feature Flags with Scope, Storage, and Custom Drivers](https://cdn.msaied.com/212/ab98aa676ce445275d736755a046b360.png) laravel feature-flags pennant 

### Laravel Pennant: Feature Flags with Scope, Storage, and Custom Drivers

Laravel Pennant ships with more power than most teams use. Learn how to scope flags to tenants, swap storage d...

  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said 

 16 Jun 2026     4 min read  

  Read    

 ](https://msaied.com/articles/laravel-pennant-feature-flags-with-scope-storage-and-custom-drivers) [ ![Laravel Eloquent Global Scopes: Pitfalls, Testing, and Composing Them Safely](https://cdn.msaied.com/211/8b9b19e7ecbf690b182ffbe6bffc9530.png) laravel eloquent testing 

### Laravel Eloquent Global Scopes: Pitfalls, Testing, and Composing Them Safely

Global scopes are powerful but easy to misuse. Learn how to write, test, and safely compose Eloquent global sc...

  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said 

 16 Jun 2026     1 min read  

  Read    

 ](https://msaied.com/articles/laravel-eloquent-global-scopes-pitfalls-testing-and-composing-them-safely) [ ![Eloquent Custom Relations: Polymorphic Pivots, HasManyThrough Tricks, and Raw Join Relations](https://cdn.msaied.com/210/b47272214946c6adcd02ddf74b7df816.png) laravel eloquent database 

### Eloquent Custom Relations: Polymorphic Pivots, HasManyThrough Tricks, and Raw Join Relations

Beyond belongsTo and hasMany lies a set of underused Eloquent relation techniques. This guide covers custom re...

  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said 

 16 Jun 2026     3 min read  

  Read    

 ](https://msaied.com/articles/eloquent-custom-relations-polymorphic-pivots-hasmanythrough-tricks-and-raw-join-relations) 

   [  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MH.png)   Mohamed Said Laravel Backend Engineer  ](https://msaied.com)Senior Backend Engineer specializing in Laravel, scalable SaaS platforms, APIs, and cloud infrastructure. I build secure, high-performance web applications that help businesses grow.

Explore

- [Home](https://msaied.com)
- [Projects](https://msaied.com/projects)
- [Articles](https://msaied.com/articles)
- [Certificates](https://msaied.com/certificates)
- [Contact](https://msaied.com#contact-section)

Connect

- [   hello@msaied.com ](mailto:hello@msaied.com)
- [   +20 109 461 9204 ](tel:+201094619204)

© 2026 Mohamed Said. All rights reserved.

 [  ](https://github.com/EG-Mohamed) [  ](https://www.linkedin.com/in/msaiedm/) [  ](https://wa.me/201094619204) [  ](mailto:hello@msaied.com) [  ](https://drive.google.com/file/u/0/d/1MF20IPRJyzfy32mhEutjL5EpSls0w2Q8/view)
