Skip to content

Reverb WebSocket Configuration & Troubleshooting

Laravel Reverb provides real-time WebSocket communication for the project. This document covers key configuration points and troubleshooting methods for common issues.

Configuration Overview

Environment Variables (.env)

env
BROADCAST_CONNECTION=reverb

REVERB_APP_ID=your_app_id
REVERB_APP_KEY=your_app_key
REVERB_APP_SECRET=your_app_secret
REVERB_HOST=www.innoshop.cn
REVERB_PORT=8081
REVERB_SCHEME=https

# Production (with nginx reverse proxy): set to empty, frontend uses port 443 via nginx proxy
# Local development (no reverse proxy): do not set this variable, automatically uses REVERB_PORT for direct connection
REVERB_FRONTEND_PORT=

QUEUE_CONNECTION=redis

Reverb relies on a Redis-driven queue to dispatch broadcast events. QUEUE_CONNECTION must be set to redis.

Nginx Reverse Proxy

The Reverb service listens on an internal port (e.g., 8081) and needs to be exposed externally via Nginx reverse proxy for WebSocket connections:

nginx
# WebSocket proxy for Reverb
location /app {
    proxy_pass http://127.0.0.1:8081;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_read_timeout 86400;
}

Key points:

  • proxy_http_version 1.1 — WebSocket upgrade requires HTTP/1.1
  • Upgrade / Connection headers — Trigger the WebSocket handshake
  • proxy_read_timeout 86400 — Keep long connections alive for 24 hours to prevent Nginx from closing them

Bootstrap Route Registration

bootstrap/app.php needs to register the channels route file, otherwise broadcast channel authorization will not take effect:

php
return Application::configure(basePath: dirname(__DIR__))
    ->withRouting(
        web: __DIR__.'/../routes/web.php',
        commands: __DIR__.'/../routes/console.php',
        channels: __DIR__.'/../routes/channels.php',  // Must be added
        health: '/up',
    )

Frontend Echo Component

The Echo component is located at innopacks/common/resources/views/components/echo.blade.php and is included in layouts via @include('common::components.echo').

All three layouts already include it:

  • innopacks/front/resources/views/layouts/app.blade.php
  • innopacks/panel/resources/views/layouts/app.blade.php
  • innopacks/seller/resources/views/seller/layouts/app.blade.php

Frontend JS assets need to be published to public/vendor/:

  • public/vendor/laravel-echo/echo.iife.js
  • public/vendor/pusher-js/pusher.min.js

Supervisor Process Management

Reverb needs to run as a persistent process, managed by Supervisor (see Supervisor):

ini
[program:innocn-reverb]
command=php /www/wwwroot/www.innoshop.cn/artisan reverb:start --port=8081
autostart=true
autorestart=true

Common Issues

1. curl Test Returns 500 / Internal Server Error

Symptom: Accessing https://example.com/app/your-key directly with curl returns 500.

Cause: Reverb only accepts WebSocket upgrade requests. Regular HTTP requests (without the Upgrade: websocket header) are rejected and return 500. This is normal behavior and does not indicate a Reverb malfunction.

Verification: Use a PHP script to simulate a WebSocket handshake:

php
<?php
$sock = stream_socket_client("tcp://127.0.0.1:8081", $errno, $errstr, 5);
$key = base64_encode(random_bytes(16));
$headers = "GET /app/your_app_key HTTP/1.1\r\n"
    . "Host: 127.0.0.1:8081\r\n"
    . "Upgrade: websocket\r\n"
    . "Connection: Upgrade\r\n"
    . "Sec-WebSocket-Key: $key\r\n"
    . "Sec-WebSocket-Version: 13\r\n\r\n";
fwrite($sock, $headers);
stream_set_timeout($sock, 3);
echo fread($sock, 4096);
fclose($sock);

Expect to see 101 Switching Protocols and pusher:connection_established.

2. channels.php Not Registered Causes Channel Authorization 404

Symptom: WebSocket connection succeeds, but subscribing to a private channel returns 404.

Cause: The channels route file was not registered in bootstrap/app.php, so the channel authorization callbacks in routes/channels.php do not take effect.

Solution: Add the channels parameter in withRouting():

php
->withRouting(
    // ...
    channels: __DIR__.'/../routes/channels.php',
)

3. HTTP/2 Causes WebSocket Upgrade Failure

Symptom: Direct connection to the Reverb port works fine, but the WebSocket handshake fails through the Nginx HTTPS proxy.

Cause: Nginx is configured with http2 on;, but WebSocket upgrade requires HTTP/1.1. Browsers automatically handle protocol negotiation (initiating WebSocket requests with HTTP/1.1 first), but some clients or testing tools may have issues.

Note: In practice, mainstream browsers automatically downgrade to HTTP/1.1 when initiating WebSocket connections, so http2 on; typically does not affect normal frontend usage. If you do encounter issues, you can force HTTP/1.1 in the /app location (the Nginx proxy configuration already includes proxy_http_version 1.1).

4. Missing Frontend JS Assets

Symptom: Browser console reports echo.iife.js or pusher.min.js 404.

Solution: Publish the frontend assets:

bash
php artisan vendor:publish --tag=laravel-echo
php artisan vendor:publish --tag=pusher-js

Or manually confirm that public/vendor/laravel-echo/echo.iife.js and public/vendor/pusher-js/pusher.min.js exist.

5. Echo Not Initialized

Symptom: After page load, window.Echo is undefined, and the console shows a [Reverb] Echo initialization failed warning.

Troubleshooting:

  1. Check that .env has BROADCAST_CONNECTION=reverb
  2. Check that the layout file includes @include('common::components.echo')
  3. Check that config('broadcasting.connections.reverb') values are correct
  4. Clear the config cache: php artisan config:clear

6. Queue Not Running Causes Broadcast Events to Be Lost

Symptom: The server triggers a broadcast event, but the frontend does not receive it.

Cause: Broadcast events are dispatched asynchronously through the queue. If QUEUE_CONNECTION is not redis or the queue worker is not running, events will be discarded.

Solution:

  1. Confirm QUEUE_CONNECTION=redis in .env
  2. Confirm that the Horizon / queue:work process is running
  3. Check the Horizon Dashboard (/horizon) for failed jobs

7. Frontend Directly Connecting to Reverb Port (Bypassing nginx Reverse Proxy)

Symptom: The browser connects to wss://www.innoshop.cn:8081/app/... instead of wss://www.innoshop.cn/app/.... Some network environments may block non-standard ports, causing connection failures.

Cause: The Echo component directly uses REVERB_PORT (8081) as the frontend connection port. In production with an nginx reverse proxy, the frontend should go through port 443, with nginx forwarding to the internal Reverb.

Solution: Control this via the REVERB_FRONTEND_PORT environment variable:

Scenario.env ConfigurationFrontend Connection Address
Production (with nginx reverse proxy)REVERB_FRONTEND_PORT= (empty value)wss://www.innoshop.cn/app/... (via 443)
Local development (no reverse proxy)Do not set this variable, automatically uses REVERB_PORTws://localhost:8080/app/... (direct)

The Echo component checks REVERB_FRONTEND_PORT. When empty, it does not output wsPort/wssPort, and Pusher JS automatically uses the default port 443.

8. Port Conflict

Symptom: reverb:start fails to start or reports the port is already in use.

Troubleshooting:

bash
ss -tlnp | grep 8081

If the port is occupied by another process, modify REVERB_PORT in .env and restart.

When deploying multiple projects, ensure each project uses a different port (e.g., Factory uses 8080, innoshop.cn uses 8081).

Quick Verification Checklist

When encountering Reverb issues, troubleshoot in the following order:

  1. Is the process running: ps aux | grep reverb:start
  2. Is the port listening: ss -tlnp | grep <port>
  3. Is direct connection working: Test with PHP WebSocket handshake script at 127.0.0.1:<port>
  4. Is Nginx proxy working: Test with PHP WebSocket handshake script at ssl://domain:443
  5. Are environment variables correct: php artisan config:show broadcasting
  6. Do frontend assets exist: ls public/vendor/laravel-echo/ public/vendor/pusher-js/
  7. Does layout include Echo: grep -r "components.echo" innopacks/*/resources/views/layouts/
  8. Is the queue running: php artisan horizon:status or check Supervisor