Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

Preavee's avatar

Laravel Websockets + Cloudflare + Digital Ocean won't work!

Hello everyone, did anyone here ever got laravel websockets + cloudflare + do to work? Recently migratet my dns and proxy to cloudflare for the protection but since then my websockets don't work anymore, they where working fine for over a year prior. So here is what I got and what I tried. (I really have no idea anymore). Im really thankfull for help, a config that worked for some of you with that combination or more ideas to troubleshoot this whole thing ...

The error message :)

app-jicTCnNS.js:39 WebSocket connection to 'wss://demo.com:2053/app/demo?protocol=7&client=js&version=8.3.0&flash=false' failed: WebSocket is closed before the connection is established.

Laravel Websockets & pusher config:

// bootstrap.js
window.Echo = new Echo({
    broadcaster: "pusher",
    key: "demo",
    cluster: "mt1",
    wsHost: window.location.hostname,
    wssHost: window.location.hostname,
    wsPort: 2053,
    wssPort: 2053,
    encrypted: true,
    disableStats: false,
    forceTLS: true,
    enabledTransports: ["ws", "wss"],
});
<?php
// websockets.php
use BeyondCode\LaravelWebSockets\Dashboard\Http\Middleware\Authorize;

return [
    'dashboard' => [
        'port' => env('LARAVEL_WEBSOCKETS_PORT', 6001),
    ],

    'apps' => [
        [
            'id' => env('PUSHER_APP_ID'),
            'name' => env('APP_NAME'),
            'key' => env('PUSHER_APP_KEY'),
            'secret' => env('PUSHER_APP_SECRET'),
            'path' => env('PUSHER_APP_PATH'),
            'capacity' => null,
            'enable_client_messages' => false,
            'enable_statistics' => true,
        ],
    ],

    'app_provider' => BeyondCode\LaravelWebSockets\Apps\ConfigAppProvider::class,
    'allowed_origins' => [],
    'max_request_size_in_kb' => 250,
    'path' => 'laravel-websockets',
    'middleware' => [
        'web',
        Authorize::class,
    ],

    'statistics' => [
        'model' => \BeyondCode\LaravelWebSockets\Statistics\Models\WebSocketsStatisticsEntry::class,
        'logger' => BeyondCode\LaravelWebSockets\Statistics\Logger\HttpStatisticsLogger::class,
        'interval_in_seconds' => 300,
        'delete_statistics_older_than_days' => 60,
        'perform_dns_lookup' => false,
    ],

    'ssl' => [
        'local_cert' => env('LARAVEL_WEBSOCKETS_SSL_LOCAL_CERT', '/etc/letsencrypt/live/mykey/fullchain.pem'),
        'local_pk' => env('LARAVEL_WEBSOCKETS_SSL_LOCAL_PK', '/etc/letsencrypt/live/mykey/privkey.pem'),

        'passphrase' => env('LARAVEL_WEBSOCKETS_SSL_PASSPHRASE', null),
        'verify_peer' => false,
        'verify_peer_name' => false,
        'allow_self_signed' => true,
    ],

    'channel_manager' => \BeyondCode\LaravelWebSockets\WebSockets\Channels\ChannelManagers\ArrayChannelManager::class,
];
<?php

return [
    'default' => env('BROADCAST_DRIVER', 'null'),

    'connections' => [
        'pusher' => [
            'driver' => 'pusher',
            'key' => env('PUSHER_APP_KEY'),
            'secret' => env('PUSHER_APP_SECRET'),
            'app_id' => env('PUSHER_APP_ID'),
            'options' => [
                'encrypted' => env('LARAVEL_WEBSOCKETS_ENCRYPTED', TRUE),
                'useTLS' => env('LARAVEL_WEBSOCKETS_USETLS', TRUE),
                'cluster' => env('PUSHER_APP_CLUSTER'),
                'host' => env('LARAVEL_WEBSOCKETS_HOST', 'demo'),
                'port' => env('LARAVEL_PUSHER_PORT', 443),
                'scheme' => env('LARAVEL_PUSHER_SCHEME', "https"),
                'curl_options' => [
                    CURLOPT_SSL_VERIFYHOST => 0,
                    CURLOPT_SSL_VERIFYPEER => 0,
                ],
            ],
        ],
		
		 ....
    ],
];
// composer.json
        "pusher/pusher-php-server": "^7.2.4",
        "beyondcode/laravel-websockets": "^1.14.1",
// package.json
        "laravel-echo": "^1.16.1",
        "pusher-js": "^8.3.0",

On the Digital Ocean Droplet

nginx

server {
    server_name www.demo.com;
    return 301 $scheme://demo.com$request_uri;

    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/demo.com/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/demo.com/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}

server {
    listen 80;
    server_name demo www.demo.com;
    return 301 https://demo.com$request_uri;
}

server {
    server_name demo.com;
    root /var/www/demo/public;

    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-XSS-Protection "1; mode=block";
    add_header X-Content-Type-Options "nosniff";

    index index.html index.htm index.php;
    charset utf-8;

    if ($http_x_forwarded_proto = 'http') {
        return 301 https://$host$request_uri;
    }

    location = /favicon.ico { access_log off; log_not_found off; }
    location = /robots.txt  { access_log off; log_not_found off; }

    error_page 404 /index.php;

    location ~ \.css {
        add_header Content-Type text/css;
    }

    location ~ \.js {
        add_header Content-Type application/x-javascript;
    }

    location ~ .php$ {
        fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        include fastcgi_params;
    }

        # WebSocket Location Block - Ensure it's above the `location /` block
    location /app {
        proxy_pass http://127.0.0.1:2053;
        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_read_timeout 600;
        proxy_connect_timeout 600;
        proxy_redirect off;
    }

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

    listen 443 ssl; # managed by Certbot
    listen [::]:443 ssl;
    ssl_certificate /etc/letsencrypt/live/demo/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/demo/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}

.env

PUSHER_APP_ID=demo
PUSHER_APP_KEY=demo
PUSHER_APP_SECRET=demo
PUSHER_APP_CLUSTER=mt1
LARAVEL_WEBSOCKETS_PORT=2053
LARAVEL_PUSHER_PORT=2053
LARAVEL_WEBSOCKETS_HOST="demo.com"
LARAVEL_WEBSOCKETS_USETLS=true
LARAVEL_WEBSOCKETS_ENCRYPTED=true
LARAVEL_PUSHER_SCHEME="https"

The port is allowed in the firewall and listening:

2053 (v6)                  ALLOW       Anywhere (v6)             
2053/tcp (v6)              ALLOW       Anywhere (v6) 
php       41313            root    5u  IPv4 455135      0t0  TCP *:2053 (LISTEN)

supervisor

[program:websockets]
directory=/var/www/demo
command=sudo php artisan websockets:serve --port=2053
numprocs=1
autostart=true
autorestart=true
user=root
stderr_logfile=/var/www/demo/storage/logs/websockets.err.log
stdout_logfile=/var/www/demo/storage/logs/websockets.out.log

Cloudflare

I switched from 6001 to 2053 because its a supported port from them.

I enabled the websocket settings in the "Network" tab.

Am I missing the obvious?

0 likes
0 replies

Please or to participate in this conversation.