On this page
A remote panel does more than display traffic: it connects to a control interface that can change configurations and close connections. An open page does not establish a secure connection to that interface. Check the listening scope, authentication, and web origins separately; origin restrictions do not replace authentication. Verify that requests with no credentials or incorrect credentials are rejected before choosing a temporary tunnel or a lasting remote-access method.
Dashboard Can Change Configuration: How to Restrict Access
Check the actual listening port, such as 9090, and who can reach it in the external-controller settings. Clash Dashboard uses this REST API to read connections, traffic, proxy groups, and rules. It can also switch nodes, reload configurations, change some runtime settings, and close connections. Exposing it directly to the internet is far riskier than exposing a read-only status page.
Access scenarios and recommended entry points
| Scenario | Listening method | Access restrictions |
|---|---|---|
| Panel bundled with a local client | 127.0.0.1:9090 | A strong secret, preventing arbitrary web pages from calling the local interface |
| Administration from the home LAN | An explicit LAN address or restricted listener | The firewall allows only management devices or private subnets |
| Temporary maintenance from another location | Continue listening only on the local machine | SSH local forwarding, optionally established over Tailscale |
| Public internet service | Direct exposure is not recommended | At minimum, use a separate reverse proxy, TLS, authentication, and source restrictions |
Configure and Test Local Listening and secret Authentication
In the final active Mihomo configuration, bind external-controller to 127.0.0.1. A graphical client may generate this field through “Settings → External Controller” or an override file, so editing the raw subscription may have no effect. After reloading, inspect both the final configuration and the actual listener.
REPLACE_WITH_RANDOM_SECRET is a placeholder. Generate a long random value with a password manager, replace it in the Mihomo configuration and Dashboard connection settings, then reload. Do not use the example placeholder as a password.
external-controller: 127.0.0.1:9090
secret: "REPLACE_WITH_RANDOM_SECRET"# Windows PowerShell
Get-NetTCPConnection -LocalPort 9090 -State Listen
# Linux / OpenWrt
ss -lntp | grep ':9090'secret is the Bearer Token for the TCP controller discussed here. An empty value provides no authentication at this layer, short passwords are easier to guess, and reuse across devices increases the impact of a leak. Keep the full token out of screenshots, frontend repositories, and public troubleshooting logs.
# 无 Authorization,应返回未授权,而不是接口数据
curl -i http://127.0.0.1:9090/version
# 错误 token 也必须被拒绝
curl -i -H "Authorization: Bearer WRONG_TOKEN" http://127.0.0.1:9090/version
# 只在自己的终端中替换 REAL_TOKEN
curl -i -H "Authorization: Bearer REAL_TOKEN" http://127.0.0.1:9090/versionResults that prove authentication works
- A request without Authorization returns 401 rather than version JSON
- The correct secret can read /version
- An incorrect secret is also rejected
- Dashboard shows connections and proxy groups correctly after a refresh
- Neither the logs nor the browser address bar exposes the secret
If Dashboard will not open, do not immediately allow every CORS origin
A remotely hosted Dashboard is a web origin that sends requests to external-controller from the browser. CORS restricts which web origins can call the interface; it does not replace user authentication.
Setting allow-origins to * and also allowing private network requests makes it easier for any website to attempt access to local or LAN controller ports. Even with a secret configured, the blast radius is larger if it leaks.
First confirm the Dashboard's actual origin, such as https://dashboard.example.com, and allow only that complete origin. A change in the domain, scheme, or port creates a different origin. The UI static-file path in the configuration and the API listening address are also separate: a page that opens does not mean the control API is connected securely.
external-controller-cors:
allow-origins:
- "https://dashboard.example.com"
allow-private-network: falseFor temporary remote maintenance, use an SSH tunnel to bring 9090 to the local machine
If Mihomo runs on a home server, keep external-controller on 127.0.0.1:9090 and use SSH local forwarding to map the remote loopback port to the current computer. The browser still connects to its own 127.0.0.1, while 9090 remains invisible to the public internet.
ssh -N -L 19090:127.0.0.1:9090 [email protected]
# Dashboard 后端填写
http://127.0.0.1:19090After connecting to the server over Tailscale, still use the SSH forwarding above to reach a controller that listens only on loopback. Joining the tailnet does not automatically expose that port. Use tailnet policy to restrict which users or devices can reach the host and the port needed for forwarding, and keep the secret.
If a home router must listen on a LAN address, allow only the management computer through the firewall, not Guest Wi-Fi or the entire WAN zone.
- Your browserInitiate access only from a trusted device
- SSH forwarding (optionally over a VPN)Do not expose the controller port directly
- 127.0.0.1:9090external-controller listens only on the local machine
- MihomoValidate control requests with a secret
If listening on a LAN address is unavoidable, also restrict firewall sources and keep a sufficiently long secret.
What Reverse-Proxy Protection Does Long-Term Remote Access Need?
Deploy a reverse proxy only when you need a stable domain and long-term remote access. Mihomo's external-controller should still listen on 127.0.0.1:9090, while Nginx, Caddy, or another gateway on the same host serves HTTPS externally. Expose only the reverse-proxy port to the public internet, not 9090.
The reverse proxy should have at least valid TLS, a separate sign-in or client authentication, source restrictions, access logs, and correct forwarding for the WebSocket used by Dashboard. Keep Mihomo's secret as well; it and reverse-proxy authentication are two independent layers and cannot replace each other. Store the secret in Dashboard connection settings instead of appending it to a public URL.
If the Dashboard page and API use different domains, add the page's exact origin to CORS. After configuring it, test with an external port scan or from another device: the HTTPS domain should present a sign-in, while direct access to the server's public IP:9090 should fail to connect.
Check Controller Authentication and Access from Unauthorized Networks
Complete acceptance checks
Inspect the listener locally
Confirm that the port is bound to the intended address and that the process is the current Mihomo instance rather than an old one.
Unauthenticated access
Use curl to request /version and confirm that a missing or incorrect token returns 401.
Authenticated access
Open Dashboard, switch one test policy group, and confirm that Mihomo shows the same result.
Test from a network without permission
Test from a LAN, Guest Wi-Fi, or public address that should not have access; the connection should time out or be refused.
Restart and retest
After restarting Mihomo, confirm that the client did not overwrite the listening address, secret, or firewall rules.
If the controller may have been exposed, first tighten the listener and firewall, then rotate the secret. Next, inspect port mappings, cloud security groups, reverse-proxy access logs, and configuration-repository history. Changing only the Dashboard password cannot invalidate a leaked Bearer Token.
