DEV Community

Ahmed Ben Bettaieb
Ahmed Ben Bettaieb

Posted on

How to Configure Laravel Reverb with Nginx in Production

Real-time WebSocket communication in Laravel has never been easier thanks to Laravel Reverb — a first-party WebSocket server that integrates seamlessly with Laravel's broadcasting system. In this guide, I'll walk you through everything needed to get Reverb running behind Nginx in production, including the gotchas that cost me hours of debugging.

What We're Building

Browser (wss://yourdomain.com/app/...)
        ↓
    Nginx :443  ← handles SSL termination
        ↓
  Reverb :8080  ← internal only, managed by Supervisor

Enter fullscreen mode Exit fullscreen mode

Nginx handles all public traffic (HTTP/HTTPS), and Reverb runs quietly on an internal port — never exposed directly to the internet.

Prerequisites

Laravel 10+ application already running on Nginx
PHP 8.2+
Node.js & npm
SSL certificate (Let's Encrypt recommended)
Supervisor installed (sudo apt-get install supervisor -y)

Step 1: Install Laravel Reverb
php artisan install:broadcasting

Answer yes to all prompts. This installs the Reverb PHP package along with Laravel Echo and pusher-js on the frontend.

Step 2: Configure Your .env
This is where most people get it wrong. Here's what matters:
For HTTPS (production with domain)

`BROADCAST_CONNECTION=reverb
QUEUE_CONNECTION=sync

REVERB_APP_ID=your-app-id
REVERB_APP_KEY=your-app-key
REVERB_APP_SECRET=your-app-secret
REVERB_HOST="yourdomain.com"
REVERB_PORT=443
REVERB_SCHEME=https

Internal server config (Reverb listens on this port)

REVERB_SERVER_HOST=127.0.0.1
REVERB_SERVER_PORT=8080

VITE_REVERB_APP_KEY="${REVERB_APP_KEY}"
VITE_REVERB_HOST="${REVERB_HOST}"
VITE_REVERB_PORT="${REVERB_PORT}"
VITE_REVERB_SCHEME="${REVERB_SCHEME}"`

For HTTP (development / IP-based server)
`BROADCAST_CONNECTION=reverb
QUEUE_CONNECTION=sync

REVERB_APP_ID=your-app-id
REVERB_APP_KEY=your-app-key
REVERB_APP_SECRET=your-app-secret
REVERB_HOST="your-server-ip"
REVERB_PORT=80
REVERB_SCHEME=http

REVERB_SERVER_HOST=127.0.0.1
REVERB_SERVER_PORT=8080

VITE_REVERB_APP_KEY="${REVERB_APP_KEY}"
VITE_REVERB_HOST="${REVERB_HOST}"
VITE_REVERB_PORT="${REVERB_PORT}"
VITE_REVERB_SCHEME="${REVERB_SCHEME}"`

⚠️ Common mistake: Don't forget QUEUE_CONNECTION=sync. Without it, broadcast events may never fire even though everything else looks correct.

Step 3: Configure Nginx
Add the WebSocket proxy blocks to your Nginx server block before location `server {
listen 443 ssl;
server_name yourdomain.com;
root /var/www/your-app/public;

ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;

# WebSocket proxy for Reverb
location /app {
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "Upgrade";
    proxy_set_header Host $host;
    proxy_cache_bypass $http_upgrade;
    proxy_pass http://127.0.0.1:8080;
}

location /apps {
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "Upgrade";
    proxy_set_header Host $host;
    proxy_cache_bypass $http_upgrade;
    proxy_pass http://127.0.0.1:8080;
}

location / {
    try_files $uri $uri/ /index.php?$query_string;
}

location ~ \.php$ {
    include fastcgi_params;
    fastcgi_pass unix:/run/php/php8.3-fpm.sock;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}

location ~ /\.ht {
    deny all;
}
Enter fullscreen mode Exit fullscreen mode

}

Redirect HTTP to HTTPS

server {
listen 80;
server_name yourdomain.com;
return 301 https://$host$request_uri;
}`

Test and reload:

sudo nginx -t
sudo systemctl reload nginx
# OR for Bitnami:
sudo /opt/bitnami/nginx/sbin/nginx -t
sudo /opt/bitnami/ctlscript.sh restart nginx
Enter fullscreen mode Exit fullscreen mode

Step 4: Keep Reverb Running with Supervisor
Without Supervisor, Reverb dies the moment your SSH session closes.
sudo nano /etc/supervisor/conf.d/reverb.conf

[program:reverb]
process_name=%(program_name)s
command=php /var/www/your-app/artisan reverb:start --host=127.0.0.1 --port=8080 --no-interaction
autostart=true
autorestart=true
user=www-data
redirect_stderr=true
stdout_logfile=/var/www/your-app/storage/logs/reverb.log
stopwaitsecs=3600

Activate:

sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start reverb
sudo supervisorctl status
Enter fullscreen mode Exit fullscreen mode

You should see:

reverb    RUNNING   pid 12345, uptime 0:00:05

Enter fullscreen mode Exit fullscreen mode

Step 5: Clear Cache & Rebuild Frontend

cd /var/www/your-app

php artisan config:clear
php artisan cache:clear

npm run build
Enter fullscreen mode Exit fullscreen mode

Step 6: Verify Everything Works
Check Reverb is listening internally:

sudo ss -tlnp | grep 8080
# Should show: LISTEN 0 ... 127.0.0.1:8080
Enter fullscreen mode Exit fullscreen mode

Test the WebSocket connection using piehost.com/websocket-tester:

URL: wss://yourdomain.com/app/your-app-key
Click Connect → should say Connection established

Subscribe to a channel:

{
    "event": "pusher:subscribe",
    "data": {
        "channel": "my-channel"
    }
}
Enter fullscreen mode Exit fullscreen mode

Fire a test broadcast from tinker:

php artisan make:event TestBroadcast
Enter fullscreen mode Exit fullscreen mode

php artisan make:event TestBroadcast

Edit app/Events/TestBroadcast.php:

<?php
namespace App\Events;

use Illuminate\Broadcasting\Channel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;

class TestBroadcast implements ShouldBroadcast
{
    public function broadcastOn() {
        return new Channel('my-channel');
    }

    public function broadcastAs() {
        return 'test.event';
    }

    public function broadcastWith() {
        return ['message' => 'Hello from Reverb!'];
    }
}
Enter fullscreen mode Exit fullscreen mode

php artisan tinker
>>> event(new \App\Events\TestBroadcast());
Enter fullscreen mode Exit fullscreen mode

You should see the message arrive in the WebSocket tester instantly. ✅

Summary
The key things that make Reverb work behind Nginx:

Nginx proxies /app and /apps paths to Reverb's internal port
REVERB_HOST = your public domain (what the browser uses)
REVERB_SERVER_PORT = internal port Reverb listens on (8080)
QUEUE_CONNECTION=sync = ensures broadcasts fire immediately
Supervisor = keeps Reverb alive permanently

Happy broadcasting! 🚀

Top comments (0)