
Hey dashboard troubleshooters. This one is only about the OpenClaw dashboard not loading.
Not model calls failing. Not channels being quiet. Not "OpenClaw is broken" in the general, unhelpful sense.
The problem here is narrower: you open the browser UI and something between the page, the Gateway, and the WebSocket connection breaks. Sometimes the page is blank. Sometimes the HTML loads but the UI sits at "connecting." Sometimes the browser throws 1008, unauthorized, pairing required, origin not allowed, or a mixed-content WebSocket error.
I stopped here because treating all of those as the same bug wastes time. A blank dashboard and a working dashboard with failed agent runs are different failures. For dashboard loading, the useful question is:
Can the browser reach the Gateway dashboard, fetch its runtime config, and complete the WebSocket auth handshake on the expected URL, port, origin, and storage context?
That is the whole flow.
The OpenClaw dashboard docs say the local dashboard is served by the Gateway at http://127.0.0.1:18789/ by default, with TLS changing the browser path to https:// and the WebSocket path to wss://. The Control UI docs also make the important bit explicit: the UI is a browser app served by the Gateway and it speaks to the Gateway WebSocket on the same port.
So if the dashboard will not load, debug the page and the WebSocket before you chase unrelated OpenClaw runtime problems.
That table is the map. Now walk it in order.
Start with the boring checks. I have skipped this step before, then spent twenty minutes adjusting nginx while the Gateway was simply not listening. Good use of time, if your hobby is being humbled by ports.
Run:
openclaw gateway status
openclaw status
openclaw doctor
lsof -i :18789
curl -i http://127.0.0.1:18789/
You want to see three things:
127.0.0.1:18789.curl http://127.0.0.1:18789/ returns the dashboard HTML, not a connection error.OpenClaw's own troubleshooting runbook uses the same shape for dashboard/control UI connectivity: check gateway status, status, logs, doctor, and the JSON status output before assuming the browser is the source of the problem.
If curl cannot reach the page, stay at the Gateway layer:
openclaw gateway restart
openclaw gateway status --deep
openclaw logs --follow
Do not move to proxy settings yet. A reverse proxy cannot fix a Gateway that is not serving the dashboard in the first place.
Use the local URL first:
http://127.0.0.1:18789/
Then try:
http://localhost:18789/
They often behave the same, but not always. Browser security, origin matching, old saved settings, or a stale deep link can make one path fail while the other tells you what is actually wrong.
If you normally open the dashboard with:
openclaw dashboard
keep using that command. The official docs describe it as the recommended re-open path because it can copy/open the right link, print headless guidance, and handle shared-secret bootstrap behavior without dumping token values into logs.
One correction to older advice: do not assume the dashboard token is stored in localStorage. Current OpenClaw docs say the dashboard keeps URL tokens in sessionStorage for the current browser tab session and selected Gateway URL, then strips them from the URL after load. Passwords are not persisted across reloads.
That matters when you debug reload loops.
If the page asks for auth every time:
gateway.auth.token may be SecretRef-managed, so openclaw dashboard intentionally avoids printing or launching a tokenized URL.The fix is not "clear everything and hope." Retrieve or supply the correct shared secret from the Gateway host:
openclaw config get gateway.auth.token
Then paste the token into the dashboard settings when prompted. If no shared secret exists, generate one:
openclaw doctor --generate-gateway-token
Open DevTools before you reload the dashboard.
Go to:
Network -> WS
Then reload the page.
There are two very different outcomes.
If no WebSocket request appears, the page may not be loading the Control UI runtime correctly. Check the Console and Network tabs for:
control-ui-config.json failing.gateway.controlUi.basePath.If the WebSocket request appears and fails, click it. Look at the request URL, status, response, and close code.
Common signatures:
1008 unauthorized
1008 pairing required
origin not allowed
device nonce mismatch
AUTH_TOKEN_MISSING
AUTH_TOKEN_MISMATCH
AUTH_SCOPE_MISMATCH
gateway connect failed
Those are useful. They tell you the page loaded, but the WebSocket handshake did not complete.
The Control UI docs describe the auth paths used during the WebSocket handshake: token, password, Tailscale Serve identity headers, or trusted-proxy identity headers. So when the browser says the dashboard is disconnected, read it as a Gateway auth/identity/origin failure until proven otherwise.
1008 unauthorized is not one error. It is the browser saying, "The Gateway rejected this WebSocket connection under policy."
Check logs while reproducing:
openclaw logs --follow
Then reload the dashboard once.
If the log says AUTH_TOKEN_MISSING, the browser did not send the shared token. Paste the configured token into the dashboard settings.
If it says AUTH_TOKEN_MISMATCH, the browser sent a token, but not the one the Gateway expects. Get the current value:
openclaw config get gateway.auth.token
If it says AUTH_SCOPE_MISMATCH, the device token was recognized but does not cover the dashboard scopes being requested. Re-pair or approve the requested scope contract instead of blindly rotating the shared token.
If it says device token mismatch, your browser has stale device identity state. This can happen after browser data cleanup, changed profiles, or revoked devices.
Use:
openclaw devices list
openclaw devices approve <requestId>
For a new local loopback browser, OpenClaw generally auto-approves direct 127.0.0.1 / localhost connections. Remote, LAN, Tailnet, and some browser-profile cases can still require explicit approval.
pairing required is normal when OpenClaw sees a new device that is not already trusted.
It becomes confusing when you are convinced you are local, but the Gateway does not agree.
This happens most often in three situations:
Check logs for the remote address:
openclaw logs --follow
If your browser is local but the Gateway sees something like a Docker bridge address, the Gateway may treat the request like a non-loopback device. Approve the browser device, or adjust the deployment so the dashboard connection reaches the Gateway through the intended local path.
For remote dashboard access, prefer one of these patterns:
ssh -N -L 18789:127.0.0.1:18789 user@host
Do not expose the dashboard as a plain public admin surface. The official dashboard page calls the Control UI an admin surface because it can reach chat, config, and exec approval flows. Treat it that way.
If the browser shows connection refused, or the Gateway will not start, check the port.
lsof -i :18789
On Linux:
ss -tlnp | grep 18789
If another process owns the port, decide whether to stop that process or move OpenClaw.
Stop/restart the intended Gateway:
openclaw gateway restart
If you need a different port, update the Gateway port and then open the matching URL:
{
"gateway": {
"port": 18790
}
}
Then:
http://127.0.0.1:18790/
One small trap: changing the port in config but continuing to open an old browser bookmark is enough to make the dashboard look "broken." It is just the wrong URL.
If openclaw gateway status shows the dashboard on a Tailscale IP, LAN IP, or another interface when you expected loopback, force the safer local path.
Inspect the socket:
lsof -i :18789
For local-only use, keep the Gateway bound to loopback:
{
"gateway": {
"bind": "loopback",
"port": 18789
}
}
Then restart:
openclaw gateway restart
There is a real class of Tailscale-related confusion here. The existing GitHub issue about the Gateway binding to a Tailscale IP instead of loopback is worth keeping as context, but for dashboard loading the practical test is simple: does the browser URL match the interface the Gateway is actually serving?
If you are intentionally using Tailscale, do it as a remote access path. Do not half-use it by opening a Tailnet address in one tab, a localhost URL in another, and then debugging the wrong token/origin pair.
If the dashboard works at http://127.0.0.1:18789/ on the server but fails at https://openclaw.example.com/, you are now in reverse proxy territory.
The failure pattern is usually:
That is classic WebSocket proxy breakage.
NGINX documents the core reason in its WebSocket proxying guide: Upgrade and Connection are hop-by-hop headers, so a reverse proxy must pass them explicitly.
A minimal nginx shape:
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 443 ssl;
server_name openclaw.example.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
location / {
proxy_pass http://127.0.0.1:18789;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $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 86400;
proxy_send_timeout 86400;
}
}
Then test:
nginx -t
systemctl reload nginx
Common mistakes:
proxy_http_version 1.1.Upgrade header.Connection header.gateway.controlUi.basePath.If the browser is on HTTPS, the browser-side WebSocket must be wss://, even if nginx forwards to the Gateway over local plain ws:// or http://.
Caddy is usually less fussy for WebSockets than nginx, but it can still fail when the path or upstream is wrong.
Basic Caddyfile:
openclaw.example.com {
reverse_proxy 127.0.0.1:18789
}
The Caddy reverse_proxy docs define upstreams as backend addresses like 127.0.0.1:4000, which is exactly the pattern you want here: one public site, one local Gateway upstream.
If you serve the dashboard under a subpath:
example.com {
handle_path /openclaw/* {
reverse_proxy 127.0.0.1:18789
}
}
Make sure OpenClaw's Control UI base path agrees with that subpath. Otherwise the HTML may load while /control-ui-config.json, assets, or the WebSocket target drift back to the wrong root.
When in doubt, remove the subpath for the first working test. Get https://openclaw.example.com/ working first, then reintroduce /openclaw/.
Mixed content is brutally simple:
https://.ws://.The fix is also simple:
wss://openclaw.example.com/ws://127.0.0.1:18789/ or plain HTTP upstreamDo not paste a local ws://127.0.0.1:18789 endpoint into a dashboard loaded from an HTTPS domain and expect the browser to be relaxed about it. It will not be relaxed.
For origin not allowed, compare the browser origin to the Gateway's allowed Control UI origins.
Example:
{
"gateway": {
"controlUi": {
"allowedOrigins": ["https://openclaw.example.com"]
}
}
}
The exact config shape may vary with your OpenClaw version, so confirm against the current Gateway configuration docs before committing it. The principle is stable: the browser Origin has to be one the Gateway expects.
Avoid "*" on an admin dashboard. It makes debugging feel easier for about five minutes, then turns into a security decision you probably did not mean to make.
Clearing site data is sometimes the right fix. It is not a diagnosis.
Use it when the evidence points to stale browser-side identity or stale dashboard session state:
Try in this order:
http://127.0.0.1:18789/.openclaw dashboard.If a different browser works immediately, your Gateway is fine. The issue is browser-local dashboard state.

If none of this fixes it, collect evidence that separates dashboard loading from general runtime failure.
Gateway status:
openclaw gateway status --deep
openclaw gateway status --json
openclaw status
openclaw doctor
Gateway logs:
openclaw logs --follow
Port/socket:
lsof -i :18789
curl -i http://127.0.0.1:18789/
Browser evidence:
control-ui-config.json loads.127.0.0.1 but fails through the domain.Sanitized config:
openclaw config get gateway
Redact tokens, passwords, domains you do not want public, and any private IPs if needed.
The report should say something like:
Dashboard HTML loads at
https://openclaw.example.com/, but WS fails with1008 AUTH_TOKEN_MISMATCH. Localhttp://127.0.0.1:18789/works. nginx config includes Upgrade/Connection headers. Gateway logs attached.
That is useful. "OpenClaw not working" is not.
When I debug this now, I do not start with a theory. I run this list:
openclaw gateway status
curl -i http://127.0.0.1:18789/
lsof -i :18789
openclaw logs --follow
Then I open DevTools -> Network -> WS and reload the dashboard.
If local curl fails, I fix the Gateway.
If local curl works but the browser cannot connect, I check URL, browser storage, and auth.
If localhost works but the domain fails, I check reverse proxy WebSocket headers, TLS, origin, and base path.
If the WebSocket says 1008, I read the exact auth detail before changing anything.
That order has saved me more time than any clever fix.
At Macaron, we care about this because the boring setup layer shapes whether you ever get to test the actual workflow. If you want a hosted AI workspace where you are not spending the first hour checking WebSocket headers, create a Macaron account and test a real workflow there instead. No nginx tuning required.
Q: The OpenClaw dashboard HTML loads, but the UI stays disconnected. Where should I start? Open DevTools -> Network -> WS and reload. If the WebSocket fails, read the close code before touching config. A loaded page with a failed WebSocket is usually auth, origin, TLS, or reverse proxy upgrade headers.
Q: The browser shows 1008 unauthorized. Is my install broken?
Probably not. 1008 usually means the Gateway rejected the WebSocket connection under auth or device policy. Check openclaw logs --follow for AUTH_TOKEN_MISSING, AUTH_TOKEN_MISMATCH, AUTH_SCOPE_MISMATCH, device token mismatch, or pairing-required messages. If you are starting from a clean machine, follow the step-by-step guide to OpenClaw setup steps before adding channels, skills, or custom integrations.
Q: Why does the dashboard ask for auth again after reload?
Current OpenClaw docs say dashboard URL tokens are kept in sessionStorage for the current browser tab session and selected Gateway URL. Passwords are not persisted. A new tab, cleared site data, changed browser profile, or SecretRef-managed token can all make the dashboard ask again.
Q: The dashboard works on 127.0.0.1 but not through nginx. What is the likely fix?
Pass the WebSocket upgrade correctly: HTTP/1.1, Upgrade, Connection, and sane read timeouts. Also confirm HTTPS browser pages use wss://, not ws://.
Q: Can I expose the dashboard publicly if I add a password? I would not treat it like a normal public web app. The Control UI is an admin surface. Prefer localhost, Tailscale Serve, SSH tunnel, or a deliberately configured trusted proxy path.
Q: Should I debug model/provider/channel errors in this article's flow? No. This flow stops once the dashboard loads and the WebSocket connects. If the UI is connected but model calls fail, that is a different OpenClaw problem.