Mattermost Messages Only Appear After Refresh: Diagnosing Broken WebSocket Connections
Mattermost messages, presence, and typing indicators that only appear after a refresh point to a broken WebSocket. Diagnose the handshake, proxy timeouts, SiteURL, and clustering.
01 May 2026, 10:20 UTC

The recognizable condition
Users report that new messages, presence dots, typing indicators, and channel sidebar updates only appear after a manual page refresh. Everything else works: login, posting, search. This pattern almost always means the persistent WebSocket connection to /api/v4/websocket is failing or being torn down. The Mattermost web and desktop apps depend on that connection for real-time events; without it, the client falls back to fetching state on load, which is exactly what "works after refresh" looks like.
Open the browser's DevTools, go to the Network tab, filter by "WS", and reload the page. You will typically see either a failed handshake or a connection that dies and reconnects on a suspiciously regular interval.
Cause and diagnostic table
| Observation in DevTools / logs | Likely cause | Where to fix |
|---|---|---|
| Handshake returns 200, 403, 404, or 502 instead of 101 | Reverse proxy not forwarding the WebSocket upgrade | Proxy config (nginx, Apache, IIS, managed LB) |
| 101 succeeds, but a new connection appears every ~60 seconds | Proxy or load balancer idle timeout killing idle sockets | Timeout settings on proxy/LB |
Browser console shows mixed-content or ws:// blocked errors | HTTPS site attempting an insecure WebSocket | SiteURL / TLS configuration |
| 101 stays open, but some users miss events others receive | Clustering broken in a high-availability deployment | System Console High Availability, node logs |
| Works in a private window, fails in the normal profile | Browser extension or client-side filtering | Client machine, not the server |
Ordered checks
- Reproduce cleanly. Open the site in a private/incognito window with extensions disabled. If real-time updates work there, the problem is client-side (an extension or corporate browser policy), and you can stop touching the server.
- Inspect the handshake. In DevTools, Network tab filtered to WS, find the request to
/api/v4/websocket. The expected result is HTTP 101 Switching Protocols with a connection that stays open. Note the status code and how long the connection lives. - Compare SiteURL with the browser URL. In System Console, check Site URL (or
ServiceSettings.SiteURLinconfig.json). It must match the scheme and host users actually browse to. An HTTPS site must produce awss://WebSocket; browsers blockws://from an HTTPS page as mixed content. If aWebsocketURLoverride is set, confirm it is reachable from client machines, not just from inside the server network. - Review proxy and load balancer config. Check for upgrade headers and idle timeouts (details below).
- Check server logs. Search the Mattermost logs for
websocketandclustererrors around the time of a reported failure. - Bypass the proxy if feasible. Connect a test client directly to the Mattermost server's port. If real-time works directly but not through the proxy, the proxy path is definitively at fault.
Fix: proxy not upgrading the connection
This is the most common cause. For nginx in front of Mattermost, the location block handling the WebSocket path needs HTTP/1.1 and the upgrade headers:
location ~ /api/v[0-9]+/(users/)?websocket$ {
proxy_pass http://mattermost-backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 600s;
}
Apply this on the nginx host (requires root or sudo to edit the site config and run nginx -t && systemctl reload nginx). The risk of a bad edit is taking down all traffic, not just WebSockets, so always run nginx -t before reloading. On Apache, the equivalent requirement is mod_proxy_wstunnel. On IIS, Azure App Service, Cloudflare, and various managed load balancers and WAFs, WebSocket support often has to be explicitly enabled, and the exact knob differs per platform — check the provider's current documentation rather than assuming nginx advice transfers.
Fix: idle-timeout churn
If DevTools shows a clean 101 that reconnects on a fixed interval (about every 60 seconds is the classic signature), something in the path is closing idle connections. nginx's proxy_read_timeout defaults to 60 seconds, and many cloud load balancers have their own idle timeouts with provider-specific defaults. The client reconnects with backoff, so the app mostly works — but events arriving during a reconnect window are lost until the next refresh.
Raise the timeout on every hop you control (the proxy_read_timeout 600s; line above covers nginx) well above the client's reconnect behavior. Verify the deployed value against your Mattermost release's published proxy configuration guidance, since recommended values have changed across versions.
Fix: SiteURL and transport mismatch
Set ServiceSettings.SiteURL to the exact URL users browse, including the https:// scheme. Terminate TLS at the proxy or server so the negotiated WebSocket is wss://. This is a configuration correction, not a security workaround — do not "fix" mixed content by downgrading the site to HTTP.
Fix: high-availability event propagation
A successful 101 handshake does not guarantee delivery in a multi-node deployment. Events generated on one node reach users connected to other nodes via Mattermost's clustering. If clustering is disabled or nodes cannot reach each other, some users silently miss events. Open the High Availability section of System Console, confirm clustering is enabled and every node reports healthy, and check node logs for inter-node communication errors. Exact setting names vary by release, so confirm against the docs for your version.
Verifying the fix
Reload with DevTools open and confirm the WebSocket request returns 101 and stays open for several minutes without reconnecting. Then post a message from a second account in a second browser and confirm it appears in the first browser with no refresh. That end-to-end check is the only one that proves the whole path — client, proxy, server, and cluster — is working.
When to escalate
Escalate to your network or security team if a TLS-inspecting corporate proxy or WAF sits between users and the server and is stripping upgrade headers — that is their change to make, and it should be a reviewed exception, not a disabled control. If the handshake is a stable 101, clustering is healthy, and events still do not flow, collect a HAR file from DevTools, the relevant server log window, and your proxy configuration, and open a case with Mattermost support or the community forums. Version-specific behavior (client reconnect timing, System Console paths, default timeouts) should be verified against the documentation for your deployed release before you change anything.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.