Real-Time Updates with Laravel Reverb Broadcasting

PagibleAI CMS uses Laravel Reverb to send page, file, and shared-element changes to other open admin sessions. Editors see updates without refreshing the entire admin panel.

This guide covers setup, security, verification, and troubleshooting.

How synchronization works

Broadcasting is opt-in. After a database transaction commits, PagibleAI sends one event containing the item ID, version, status, editor, timestamps, and list data. Page events omit page body content. Shared-element events can include element version data, so all broadcast channels remain private.

Mounted admin views subscribe to cms.page, cms.element, or cms.file. Saving, publishing, restoring, or trashing an item patches its row. Adding, moving, or permanently deleting an item changes the list structure, so the view shows Updated by another user with a Refresh button. Detail views refetch through GraphQL when they have no unsaved changes.

A socket ID excludes the originating tab. Changes from MCP, APIs, other tabs, and scheduled jobs still reach connected editors. After reconnecting, detail views refetch when safe and lists show the refresh notice.

How list and tree views react

Action
What happens
List and tree view reaction
added
A page, element, or file is created
Shows a Refresh notice
saved
A draft version is saved
Patches the affected row
published
A version is published
Patches the affected row
restored
A trashed item is restored
Patches the affected row
dropped
An item is moved to the trash
Patches the affected row
moved
A page is moved within the tree
Shows a Refresh notice
purged
An item is permanently deleted
Shows a Refresh notice

Configure the CMS

Complete Install PagibleAI CMS first. Broadcasting is disabled by default. For a local Reverb server on port 8080, add these values to .env. Replace the example credentials outside local development.

CMS_BROADCAST=true
BROADCAST_CONNECTION=reverb

REVERB_APP_ID=pagible
REVERB_APP_KEY=pagible-key
REVERB_APP_SECRET=pagible-pass

REVERB_HOST=127.0.0.1
REVERB_PORT=8080
REVERB_SCHEME=http
  • CMS_BROADCAST enables CMS events, private-channel authorization, and the admin Echo client. Its default is false.

  • BROADCAST_CONNECTION selects Reverb as Laravel's broadcaster.

  • REVERB_APP_ID, REVERB_APP_KEY, and REVERB_APP_SECRET must match on the application and Reverb server.

  • REVERB_HOST, REVERB_PORT, and REVERB_SCHEME define Laravel's publishing endpoint. Reverb defaults REVERB_PORT to 443; direct local connections commonly use 8080. The browser uses this endpoint until you configure REVERB_CLIENT_*.

Install and run Reverb

Install Reverb and Laravel's broadcasting support:

php artisan install:broadcasting --reverb

The command installs Reverb, publishes its configuration, enables broadcasting, and prepares Laravel Echo. Set the address on which Reverb listens:

REVERB_APP_ID=pagible
REVERB_APP_KEY=pagible-key
REVERB_APP_SECRET=pagible-pass

REVERB_SERVER_HOST=127.0.0.1
REVERB_SERVER_PORT=8080
  • REVERB_SERVER_HOST and REVERB_SERVER_PORT set the process bind address. Use 127.0.0.1 for local-only access. Use 0.0.0.0 only when the surrounding network restricts access.

  • REVERB_SERVER_PORT only needs to equal REVERB_PORT when Laravel connects directly. A private service or reverse proxy can use a different address.

  • REVERB_MAX_REQUEST_SIZE limits HTTP publishing requests. The 10,000-byte default rejects larger requests; it does not truncate them.

  • REVERB_APP_MAX_MESSAGE_SIZE limits WebSocket messages independently. Its default is also 10,000 bytes.

Keep both defaults unless a measured custom event needs more room. Page events omit body content, but shared-element events can contain element version data.

Clear cached configuration after changing .env, then start Reverb:

php artisan optimize:clear
php artisan reverb:start

Run Reverb under a process or service manager in production. After code or configuration changes, rebuild Laravel's configuration cache and run php artisan reverb:restart; the manager should start the process again. See Laravel Reverb production configuration for proxy and capacity settings.

Verify the connection

Use debug mode on a local or test instance that is not already managed by another process:

php artisan reverb:start --debug
  • The startup message should show the configured address, such as 127.0.0.1:8080.

  • In browser developer tools, /app/{key} should return 101 Switching Protocols.

  • An authenticated /broadcasting/auth request should return HTTP 200. Unauthorized requests should fail.

  • Open two authenticated admin sessions and save a page. The other session should patch its row or show Updated by another user. A clean detail view should refetch the item.

Deploy behind a reverse proxy

Laravel and the browser can use different Reverb endpoints. Laravel publishes to a private HTTP address; browsers connect through a public TLS endpoint. This keeps publishing traffic private.

# Private endpoint Laravel uses to publish events
REVERB_HOST=reverb.internal
REVERB_PORT=8080
REVERB_SCHEME=http

# Public endpoint advertised to the browser
REVERB_CLIENT_HOST=cms.example.com
REVERB_CLIENT_PORT=443
REVERB_CLIENT_SCHEME=https

# Address the Reverb process binds to
REVERB_SERVER_HOST=0.0.0.0
REVERB_SERVER_PORT=8080

# One Reverb instance does not need Redis fan-out
REVERB_SCALING_ENABLED=false

Map the browser endpoint

Standard Reverb configuration does not read REVERB_CLIENT_*. Add this mapping to the first application entry in config/reverb.php. PagibleAI uses these options for the browser's Echo client.

'options' => [
    'host' => env('REVERB_CLIENT_HOST', env('REVERB_HOST')),
    'port' => env('REVERB_CLIENT_PORT', env('REVERB_PORT', 443)),
    'scheme' => env('REVERB_CLIENT_SCHEME', env('REVERB_SCHEME', 'https')),
    'useTLS' => env('REVERB_CLIENT_SCHEME', env('REVERB_SCHEME', 'https')) === 'https',
],
'allowed_origins' => [
    env('REVERB_CLIENT_HOST', env('REVERB_HOST')),
],
  • REVERB_HOST, REVERB_PORT, and REVERB_SCHEME define Laravel's private publishing endpoint.

  • REVERB_CLIENT_HOST, REVERB_CLIENT_PORT, and REVERB_CLIENT_SCHEME define the public browser endpoint. HTTPS produces wss://.

  • REVERB_SERVER_HOST and REVERB_SERVER_PORT define the process bind address.

  • allowed_origins should list every legitimate admin host instead of *.

Route Reverb traffic

  • Route public /app/ requests to Reverb with WebSocket upgrade headers.

  • Reverb receives publishing requests under /apps/. Keep that path private when REVERB_HOST uses a private endpoint. Route /apps/ publicly only when Laravel publishes through the public proxy.

  • Terminate TLS at the proxy and forward traffic over a trusted network.

  • One Reverb instance needs no Redis fan-out. Enable REVERB_SCALING_ENABLED and shared Redis only for multiple instances behind a load balancer.

This Nginx example exposes the browser connection. Put map in the http context, add your certificate settings, and include /apps/ only when Laravel publishes through this proxy:

map $http_upgrade $connection_upgrade {
    default upgrade;
    '' close;
}

server {
    listen 443 ssl;
    server_name cms.example.com;

    location /app/ {
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        proxy_pass http://127.0.0.1:8080;
    }

    # Only when REVERB_HOST points to this public proxy
    location /apps/ {
        proxy_set_header Host $host;
        proxy_pass http://127.0.0.1:8080;
    }
}

Secure Reverb

  • /broadcasting/auth requires an authenticated user, the matching page:view, element:view, or file:view permission, and the correct tenant. See Authorization and Permissions.

  • cms.broadcast-middleware includes the web session, authentication, and rate limiter. Multi-tenant installations must add tenancy initialization; see Multi-Tenancy SaaS Setup with stancl/tenancy.

  • Restrict allowed_origins to legitimate admin hosts.

  • Keep REVERB_APP_SECRET and other credentials outside source control. Use your host's secret manager.

  • Allow direct Reverb access only from the Laravel application and reverse proxy.

Troubleshooting

Changes don't appear in other editors' panels

Check CMS_BROADCAST, BROADCAST_CONNECTION, and matching REVERB_APP_* credentials. Rebuild the configuration cache, restart Reverb, and verify browser access to /app/ plus Laravel publishing to /apps/.

The browser uses the private Reverb hostname

Map REVERB_CLIENT_* into the application options in config/reverb.php. Rebuild the configuration cache and reload the admin page.

The WebSocket connection fails over HTTPS

Use REVERB_CLIENT_SCHEME=https and port 443. Confirm that the proxy terminates TLS and forwards /app/ with WebSocket upgrade headers.

Broadcasting fails with a payload-too-large error

REVERB_MAX_REQUEST_SIZE defaults to 10,000 bytes. Inspect the event before increasing it; large shared elements or custom events can exceed the limit. WebSocket messages use REVERB_APP_MAX_MESSAGE_SIZE.

Reverb rejects the browser origin

Add the exact admin host to allowed_origins, rebuild the configuration cache, and restart Reverb. Do not use * in production.

Do I need Redis?

Not for one Reverb instance. Enable Redis fan-out only when several instances run behind a load balancer.

Updates pause after inactivity

The admin disconnects after five minutes without channel subscriptions. It reconnects when needed, then refetches clean details and marks lists for refresh.