Laravel pusher broadcasting 이슈 정리

요 몇 일을 pusher socket broadcasting 이 안되는 이슈가 있어 정리해둔다 .. 로컬에서는 잘 돌던게 ssl 이 적용된 staging 서버에 올라가니 소켓 연결 자체가 안되었다..

결론적으론 nginx에서 6002 포트를 다이렉트로 리슨해서 proxy 처리로 6001로 보내주면 된다. (웹소켓 서버가 6001 (default) 포트로 돌고있음)

$ php artisan websockets:serve // Starting the WebSocket server on port 6001..

nginx 설정은 다음과 같다.

server {
    listen 6002 ssl http2;
    listen [::]:6002 ssl http2;
    server_name {your server domain};
    ssl_certificate {your server fullchain key}; 
    ssl_certificate_key {your server privkey};

    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;
    location / {
        proxy_pass             http://127.0.0.1:6001;
        proxy_read_timeout     60;
        proxy_connect_timeout  60;
        proxy_redirect         off;

        # Allow the use of websockets
        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;
    }

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

    location ~ \.php$ {
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
        fastcgi_index index.php;
        include fastcgi_params;
    }
    location ~ /\.(?!well-known).* {
        deny all;
    }
}

다음은 .env 파일에 들어갈 PUSHER 환경변수!

// .env
PUSHER_APP_ID={앱id}
PUSHER_APP_KEY={앱key}
PUSHER_APP_SECRET={앱 secret_key}
PUSHER_APP_CLUSTER=ap1
// config/websockets.php
...
    'apps' => [
        [
            'id' => env('PUSHER_APP_ID'),
            'name' => env('APP_NAME'),
            'key' => env('PUSHER_APP_KEY'),
            'secret' => env('PUSHER_APP_SECRET'),
            'capacity' => null,
            'enable_client_messages' => false,
            'enable_statistics' => true,
        ],
    ],
...

그 다음, 브로드캐스팅할 host 와 port 번호를 입력해준다. 이때 이미 nginx 에서 ssl 을 모두 처리한 상태이므로 http 로컬 통신으로 진행한다.

// config/broadcasting.php
...
        'pusher' => [
            'driver' => 'pusher',
            'key' => env('PUSHER_APP_KEY'),
            'secret' => env('PUSHER_APP_SECRET'),
            'app_id' => env('PUSHER_APP_ID'),
            'options' => [
                'cluster' => env('PUSHER_APP_CLUSTER'),
                'useTLS' => false,
                // 'encrypted' => true,
                'host' => '127.0.0.1',
                'port' => 6001, // 실제 php artisan websocket:serve 가 리슨하는 포트로 연결
                'scheme' => 'http',
            ],

            'client_options' => [
                // for self signed ssl cert
                'verify' => false, // <- Added this
                // Guzzle client options: https://docs.guzzlephp.org/en/stable/request-options.html
            ],
        ],
...

외부(프론트) 에서 접근하는 코드는 아래와 같다. APP_DEBUG 상태를 window 객체에 저장하는 코드를 blade 에 삽입한다 .

    <script>
        window.PUSHER_APP_KEY = '{{ config('broadcasting.connections.pusher.key') }}';
        window.APP_DEBUG = {{ config('app.debug') ? 'true' : 'false' }};
    </script>

그런 이후, APP_DEBUG 상태에 따라 다른 포트로 호출한다. 디버그가 true 이면 ssl 이 없는 local, false 이면 ssl 이 적용된 production 이다.

사실 다른 상태값으로 production 인지 아닌지 구분해도 큰 상관은 없다.

// resources/js/bootstrap.js

window.Echo = new Echo({
    broadcaster: 'pusher',
    key: window.PUSHER_APP_KEY,
    wsHost: window.location.hostname,
    cluster: "ap1",
    /*
    * window.APP_DEBUG - true : local
    * window.APP_DEBUG - false : production
    */
    wsPort: window.APP_DEBUG ? 6001 : 6002,
    wssPort: window.APP_DEBUG ? 6001 : 6002,
    disableStats: true,
    forceTLS: !window.APP_DEBUG,
    // encrypted: true,
    enabledTransports: ['ws', 'wss'],
});

도메인/laravel-websockets 경로로 가서 정상 동작하는지 확인!


마치며 ..

아직 이해가 되지 않는 부분은.. 처음에 ssl 없이 http로써 트레픽을 핸들링하기 위해 nginx에서 443으로 listen 하고 특정 path로 들어오는 트레픽은 프록시로 로컬 소켓서버에 전달하였다.

이렇게 계속 시도했는데, 되질 않아서 nginx에서 6002를 Listen 하고 소켓 서버 포트인 6001로 전달하니까 된다… 이 두가지 방법에 무슨 차이가 있는거지 ? 뭔가 사이에 놓친게 있는건가 ?_? ..

  • nginx 기본 ssl 포트 443 에서 특정 path 로 들어오는 요청은 소켓 서버 6001로 프록시
    • 실패
  • nginx 에 6002 를 직접 listen 하고 들어오는 모든 요청을 소켓서버 6001로 프록시

참고 url