1. Problem
When Discourse is configured with a CDN, a common error occurs:
/message-bus/204234de907442e8b77e153786a58e5b/poll
Connection failed / timed out / abnormal status code
Impact:
-
Notifications fail: The red dot (new PM/reply) no longer appears in real time; updates may arrive late or not at all.
-
Real-time updates break: New posts/likes/votes don’t auto-refresh; manual page refresh required.
-
User experience drops: “Connection lost” messages appear; interactions lag.
-
Increased server load: Frontend keeps retrying polls, adding pressure to the origin.
Reason: Discourse uses long polling to maintain real-time communication. Many CDNs enforce default caching, shortened timeouts, challenges/firewall checks, or buffering on long connections, causing interruptions or cached responses that break real-time behavior.
2. General Approach (Stability First)
-
Domain separation: Route MessageBus through a dedicated domain that connects directly to the origin.
-
Forum domain (via CDN): https://bbs.example.com
-
MessageBus domain (no CDN): https://messagebus.example.com
-
-
Reverse proxy (Nginx) layer:
-
Enable CORS for /message-bus.
-
Disable proxy buffering, relax timeouts, explicitly forbid caching.
-
-
CDN layer (if still used):
-
Configure /message-bus/* with no caching, extended timeout, no JS challenges/CAPTCHA/rate limiting, and cookie/authorization passthrough.
-
Or bypass CDN entirely.
-
3. Implementation Steps
1) Configure Discourse Environment Variables
Edit app.yml (usually at /var/discourse/containers/app.yml) and add/modify under env::
env:
DISCOURSE_MESSAGE_BUS_REDIS_ENABLED: true
DISCOURSE_LONG_POLLING_BASE_URL: "https://messagebus.example.com"
Apply changes (official deployment):
cd /var/discourse
./launcher rebuild app
Explanation:
-
DISCOURSE_LONG_POLLING_BASE_URL tells the frontend to use the MessageBus domain.
-
REDIS_ENABLED should remain enabled.
2) DNS & Certificate
-
Point messagebus.example.com directly to the origin, bypassing CDN (best practice).
-
Set up a valid HTTPS certificate for the domain.
3) Nginx (MessageBus domain) Reverse Proxy & CORS
Add or update the following in the server block for messagebus.example.com:
location ^~ /message-bus {
# (1) Handle CORS preflight (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) Reverse proxy to Discourse
proxy_pass http://unix:/var/discourse/shared/standalone/nginx.http.sock:;
# or, if standalone:
# proxy_pass http://127.0.0.1:3000;
# (3) Prevent duplicate CORS headers
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) Normal CORS for requests
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) Long polling stability settings
proxy_read_timeout 120s;
proxy_send_timeout 120s;
proxy_connect_timeout 60s;
proxy_buffering off;
add_header X-Accel-Buffering no always;
# (6) Explicitly forbid caching
add_header Cache-Control "no-store, no-cache, must-revalidate" always;
}
Security note: If Access-Control-Allow-Credentials: true is used, Origin must not be *; it must match the exact forum domain.
4) CDN Rules (if forum still behind CDN)
Recommended: set no cache + extended timeout + bypass WAF/rate-limiting for these paths:
Regex example:
^/(session|login|message-bus|admin|u|users)(/|$)
Policy:
-
No browser/node caching (no-store/no-cache).
-
Upstream/read/idle timeout ≥ 60–120s.
-
Disable JS challenge/CAPTCHA/bot management.
-
Pass through cookies & Authorization headers (do not strip).
4. How to Verify Success
1) Browser DevTools → Network
On the forum page:
-
Observe /message-bus/…/poll.
-
Request should “hang” for ~20–60s, then return 200 (possibly empty).
-
Next poll request is triggered automatically.
Check response headers:
-
Access-Control-Allow-Origin: https://bbs.example.com
-
Cache-Control: no-store
-
No Age, X-Cache: HIT, or CF-Cache-Status: HIT (means not cached).
Common issues:
-
Fixed 10s/30s errors → edge/origin timeout.
-
504/524: timeout.
-
499: intermediate layer disconnect.
-
403/401: WAF/auth blocking.
2) Command-Line Quick Probe (optional)
Check connectivity & headers (not full polling):
curl -I "https://messagebus.example.com/message-bus/health-check" \
-H "Origin: https://bbs.example.com"
Note: Actual polls require session context; this only verifies CORS & connectivity.