Discourse 포럼 /message-bus 또는 롱 폴링 오류 수정 방법

1. 문제

Discourse가 CDN으로 구성되면 다음과 같은 일반적인 오류가 발생합니다:

/message-bus/204234de907442e8b77e153786a58e5b/poll
Connection failed / timed out / abnormal status code

영향:

  • 알림 실패: 빨간 점(새로운 DM/답장)이 실시간으로 더 이상 표시되지 않으며, 업데이트가 늦게 도착하거나 전혀 도착하지 않을 수 있습니다.

  • 실시간 업데이트 중단: 새 게시물/좋아요/투표가 자동 새로고침되지 않으며, 수동으로 페이지를 새로고침해야 합니다.

  • 사용자 경험 저하: “연결 끊김” 메시지가 표시되고 상호작용이 지연됩니다.

  • 서버 부하 증가: 프론트엔드가 폴링을 계속 재시도하여 원본 서버에 부하를 가합니다.

원인: Discourse는 실시간 통신을 유지하기 위해 롱 폴링(long polling)을 사용합니다. 많은 CDN이 기본 캐싱, 줄어든 타임아웃, 챌린지/방화벽 검사 또는 롱 커넥션에 대한 버퍼링을 강제하여 중단이나 실시간 동작을 깨뜨리는 캐시된 응답을 유발합니다.


2. 일반적인 접근 방식 (안정성 우선)

  • 도메인 분리: MessageBus를 원본 서버에 직접 연결되는 전용 도메인을 통해 라우팅합니다.

  • 리버스 프록시 (Nginx) 계층:

    • /message-bus에 대해 CORS를 활성화합니다.

    • 프록시 버퍼링을 비활성화하고, 타임아웃을 완화하며, 캐싱을 명시적으로 금지합니다.

  • CDN 계층 (사용 중인 경우):

    • /message-bus/*에 대해 캐싱 없음, 확장된 타임아웃, JS 챌린지/CAPTCHA/레이트 리미팅 없음, 쿠키/인가 패스스루를 구성합니다.

    • 또는 CDN을 완전히 우회합니다.


3. 구현 단계

1) Discourse 환경 변수 구성

app.yml 파일(보통 /var/discourse/containers/app.yml에 위치)을 편집하고 env: 아래에 추가/수정합니다:

env:
  DISCOURSE_MESSAGE_BUS_REDIS_ENABLED: true
  DISCOURSE_LONG_POLLING_BASE_URL: "https://messagebus.example.com"

변경 사항을 적용합니다 (공식 배포):

cd /var/discourse
./launcher rebuild app

설명:

  • DISCOURSE_LONG_POLLING_BASE_URL은 프론트엔드가 MessageBus 도메인을 사용하도록 지시합니다.

  • REDIS_ENABLED는 활성화된 상태로 유지해야 합니다.


2) DNS & 인증서

  • messagebus.example.com을 CDN을 우회하여 원본 서버로 직접 가리키도록 설정합니다 (베스트 프랙티스).

  • 해당 도메인에 대해 유효한 HTTPS 인증서를 설정합니다.


3) Nginx (MessageBus 도메인) 리버스 프록시 & CORS

messagebus.example.com의 서버 블록에 다음을 추가하거나 업데이트합니다:

location ^~ /message-bus {

    # (1) CORS 프리플라이트 (OPTIONS) 처리
    if ($request_method = OPTIONS) {
        add_header 'Access-Control-Allow-Origin' 'https://bbs.example.com' always;
        add_header 'Access-Control-Allow-Credentials' 'true' always;
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
        add_header 'Access-Control-Allow-Headers' 'Accept,Authorization,Cache-Control,Content-Type,DNT,If-Modified-Since,Keep-Alive,Origin,User-Agent,X-Requested-With,discourse-deferred-track-view-topic-id,discourse-present,discourse-track-view,discourse-deferred-track-view,x-silence-logger,dont-chunk,x-shared-session-key' always;
        add_header 'Access-Control-Max-Age' 1728000 always;
        add_header 'Content-Type' 'text/plain; charset=UTF-8' always;
        add_header 'Content-Length' 0 always;
        return 204;
    }

    # (2) Discourse로 리버스 프록시
    proxy_pass http://unix:/var/discourse/shared/standalone/nginx.http.sock:;
    # 또는, 스탠드얼론인 경우:
    # proxy_pass http://127.0.0.1:3000;

    # (3) 중복 CORS 헤더 방지
    proxy_hide_header Access-Control-Allow-Origin;
    proxy_hide_header Access-Control-Allow-Credentials;
    proxy_hide_header Access-Control-Allow-Methods;
    proxy_hide_header Access-Control-Allow-Headers;
    proxy_hide_header Access-Control-Max-Age;

    # (4) 일반 요청에 대한 CORS
    add_header 'Access-Control-Allow-Origin' 'https://bbs.example.com' always;
    add_header 'Access-Control-Allow-Credentials' 'true' always;
    add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
    add_header 'Access-Control-Allow-Headers' 'Accept,Authorization,Cache-Control,Content-Type,DNT,If-Modified-Since,Keep-Alive,Origin,User-Agent,X-Requested-With,discourse-deferred-track-view-topic-id,discourse-present,discourse-track-view,discourse-deferred-track-view,x-silence-logger,dont-chunk,x-shared-session-key' always;

    # (5) 롱 폴링 안정성 설정
    proxy_read_timeout    120s;
    proxy_send_timeout    120s;
    proxy_connect_timeout 60s;

    proxy_buffering off;
    add_header X-Accel-Buffering no always;

    # (6) 캐싱 명시적 금지
    add_header Cache-Control "no-store, no-cache, must-revalidate" always;
}

:warning: 보안 참고: Access-Control-Allow-Credentials: true를 사용하는 경우, Origin은 *일 수 없으며, 정확한 포럼 도메인과 일치해야 합니다.


4) CDN 규칙 (포럼이 여전히 CDN 뒤에 있는 경우)

권장 사항: 다음 경로에 대해 캐시 없음 + 확장된 타임아웃 + WAF/레이트 리미팅 우회를 설정합니다:

정규식 예시:

^/(session|login|message-bus|admin|u|users)(/|$)

정책:

  • 브라우저/노드 캐싱 없음 (no-store/no-cache).

  • 업스트림/읽기/유휴 타임아웃 ≥ 60–120초.

  • JS 챌린지/CAPTCHA/봇 관리 비활성화.

  • 쿠키 및 Authorization 헤더 패스스루 (제거하지 않음).


4. 성공 여부 확인 방법

1) 브라우저 DevTools → Network

포럼 페이지에서:

  • /message-bus/…/poll을 관찰합니다.

  • 요청이 ~20–60초 동안 “걸려” 있다가 200을 반환해야 합니다 (빈 값일 수 있음).

  • 다음 폴링 요청이 자동으로 트리거됩니다.

응답 헤더 확인:

  • Access-Control-Allow-Origin: https://bbs.example.com

  • Cache-Control: no-store

  • Age, X-Cache: HIT 또는 CF-Cache-Status: HIT가 없어야 합니다 (캐시되지 않았음을 의미).

일반적인 문제:

  • 고정된 10초/30초 오류 → 엣지/원본 타임아웃.

  • 504/524: 타임아웃.

  • 499: 중간 계층 연결 끊김.

  • 403/401: WAF/인가 차단.


2) 명령줄 빠른 프로브 (선택 사항)

연결성 및 헤더 확인 (완전한 폴링 아님):

curl -I "https://messagebus.example.com/message-bus/health-check" \
  -H "Origin: https://bbs.example.com"

참고: 실제 폴링에는 세션 컨텍스트가 필요합니다. 이 명령은 CORS 및 연결성만 확인합니다.

2개의 좋아요