What is Laravel Reverb, how does it sit between a Laravel app and Echo, and why does it speak the Pusher protocol?
answer
- a long-running Artisan process
- reverb:start on 0.0.0.0:8080
- Pusher protocol, pusher-js client
- the reverb driver is the Pusher driver
- key is public, secret signs
basics
~20 sReverb is Laravel's first-party WebSocket server, run as a long-lived php artisan reverb:start process. It implements the Pusher protocol, so Laravel broadcasts to it through the Pusher driver and browsers connect with Echo and pusher-js, identified by REVERB_APP_KEY.
solid answer
~40 sReverb is a WebSocket server written in PHP on a ReactPHP event loop and started with `php artisan reverb:start`, which binds `0.0.0.0:8080` by default and keeps running like a queue worker. It sits in the middle: the Laravel app sends each broadcast to Reverb's HTTP API at `/apps/{appId}/events`, and Reverb pushes it to the browsers connected at `/app/{appKey}`. Because it implements the **Pusher Channels protocol**, nothing new is needed on either side: the framework's `reverb` broadcast driver is the Pusher driver pointed at your own host, and Echo uses `broadcaster: 'reverb'` with `pusher-js`. Three credentials tie them together: `REVERB_APP_ID`, `REVERB_APP_KEY` (public, it is in the browser's URL) and `REVERB_APP_SECRET` (private, it signs the app's API calls and channel authorizations). `allowed_origins` in `config/reverb.php`, `['*']` by default, limits which sites' pages may connect.
code
ini · 13 linesBROADCAST_CONNECTION=reverb
REVERB_APP_ID=482113
REVERB_APP_KEY=whiteboard-key
REVERB_APP_SECRET=change-me
REVERB_HOST="localhost"
REVERB_PORT=8080
REVERB_SCHEME=http
VITE_REVERB_APP_KEY="${REVERB_APP_KEY}"
VITE_REVERB_HOST="${REVERB_HOST}"
VITE_REVERB_PORT="${REVERB_PORT}"
VITE_REVERB_SCHEME="${REVERB_SCHEME}"go deeper
Recall that Reverb is a WebSocket server started with reverb:start and that Echo connects to it using the Pusher protocol.
Explain the two paths through Reverb, the HTTP API from the app and the WebSocket from the browser, and which credential each uses.
Treat the secret as a broadcast credential, restrict allowed_origins, and plan Reverb as its own long-running service beside web and queue processes.
Weigh running Reverb yourself against a hosted Pusher-protocol service: connection volume, operations effort, failure handling and the lock-in each avoids.
## What Reverb is **Laravel Reverb** is a first-party WebSocket server for Laravel applications. Instead of paying a hosted service to hold browser connections, you run the server yourself as part of the application: - it is a Composer package (`laravel/reverb`), installed by `php artisan install:broadcasting` with the Reverb option or by `reverb:install` - it runs as a **long-lived Artisan process**, `php artisan reverb:start`, built on a ReactPHP event loop, so one PHP process holds many connections - by default it binds `0.0.0.0:8080`; `--host`, `--port`, `--hostname` and `--path` override the config, and `--debug` prints the traffic For a collaborative whiteboard, Reverb is what keeps every participant's browser connected, so a stroke drawn by one user appears on the others' canvases immediately. ## Where it sits Broadcasting has three parties, and Reverb is the one in the middle: 1. The **Laravel app** dispatches a broadcast event. The broadcast job calls Reverb's **HTTP API** (`POST /apps/{appId}/events`), signed with the app secret. 2. **Reverb** receives it and pushes the message to every connection subscribed to the channel. 3. **Browsers** hold a WebSocket open to Reverb at **`/app/{appKey}`** and receive the message through Echo. Private and presence subscriptions still go through the Laravel app: Echo asks `/broadcasting/auth`, the app signs the subscription with the secret, and Reverb verifies the signature. ## Why the Pusher protocol Reverb implements the **Pusher Channels protocol**, the message format and HTTP API that the hosted Pusher service uses. That choice is what makes it cheap to adopt: - on the server, the framework's `reverb` broadcast driver is literally the Pusher driver pointed at your own host, using the Pusher PHP SDK - on the client, Echo's `reverb` broadcaster uses the `pusher-js` library - moving between hosted Pusher and Reverb is mostly a change of `BROADCAST_CONNECTION` and connection settings; the events, channels and Echo listeners stay the same | Piece | Talks to Reverb over | Configured by | |---|---|---| | Laravel app (broadcast driver) | HTTP API at `/apps/...` | `config/broadcasting.php`: `REVERB_APP_ID`, `REVERB_APP_KEY`, `REVERB_APP_SECRET`, `REVERB_HOST`, `REVERB_PORT`, `REVERB_SCHEME` | | Browser (Echo + pusher-js) | WebSocket at `/app/{key}` | `VITE_REVERB_APP_KEY`, `VITE_REVERB_HOST`, `VITE_REVERB_PORT`, `VITE_REVERB_SCHEME` | | Reverb server | — | `config/reverb.php`: bind address, apps, scaling | ## The credentials - **`REVERB_APP_ID`** names the application in API URLs. - **`REVERB_APP_KEY`** is **public**: it appears in the WebSocket URL every browser opens, and Reverb uses it to find the application. Treat it as an identifier, not a password. - **`REVERB_APP_SECRET`** is **private**: it signs the app's API requests and the channel authorizations. Anyone holding it can broadcast to your users or forge subscriptions. One Reverb installation can serve several applications by listing more entries under `apps` in `config/reverb.php`, each with its own credentials. ## Restricting origins Each app entry has an `allowed_origins` list. The shipped config uses `['*']`, which accepts a connection from any page. Setting it to your hosts, such as `['whiteboard.example.com']` (wildcard patterns like `*.example.com` are accepted), makes Reverb reject browser connections whose `Origin` host is not listed. It is a browser-facing control; it does not replace channel authorization. ## Hooks into the server's life Reverb dispatches its own Laravel events while it runs, such as `Laravel\Reverb\Events\ChannelCreated` when the first client subscribes to a channel, `ChannelRemoved` when the last one leaves, `ConnectionPruned` for stale connections, and `MessageReceived` / `MessageSent` for traffic. They are dispatched inside the Reverb process, so listeners for them run there and should stay fast; a slow listener blocks the event loop for every connection. ## Common misconceptions - Reverb is not a queue worker; broadcast jobs still need `queue:work` in the app - Reverb does not run inside PHP-FPM requests; it is its own process - the app key is not a secret, the app secret is
- Can an app that used hosted Pusher Channels move to Reverb without changing its events or Echo listeners?Largely yes. Reverb speaks the same protocol, so events, channels and `Broadcast::channel` callbacks stay. You install Reverb, switch `BROADCAST_CONNECTION` to `reverb`, set the `REVERB_*` credentials and host, and point Echo at `broadcaster: 'reverb'` with the Reverb host and key.
- Is it a problem that REVERB_APP_KEY appears in the browser's JavaScript?No. The key only identifies which application a connection belongs to, and it is in the WebSocket URL by design. What must stay private is `REVERB_APP_SECRET`, which signs broadcasts and channel authorizations; leaking it lets anyone push messages to your users.
saying these in an interview costs you the question
- REVERB_APP_KEY must be kept secret because it lets clients broadcast.
- Reverb runs inside the normal PHP-FPM request cycle.
- Reverb needs its own client library instead of pusher-js.
- With Reverb installed, broadcast events no longer need a queue worker.
- The shipped allowed_origins setting already restricts connections to the app's domain.