Start your 3-day free trial
Sign up to experience all premium features at no cost.
*Available only to new users. Each user is limited to one trial.


The Mihomo controller API is a management interface for the proxy core, so its listener and credentials deserve separate protection from the proxy traffic ports. Bind the HTTP controller narrowly, require a strong Secret, use a dashboard you trust, and verify both unauthorized rejection and authorized access. A page loading in a browser is not evidence that the management interface is secure.[1]
Key Takeaways
- Proxy authentication and HTTP controller authentication protect different interfaces.
- Loopback reduces exposure but does not make browser code or local processes trustworthy.
- Unix sockets and Windows Named Pipes do not validate the HTTP API Secret.
- Remote administration needs a controlled entry, protected transport, and a tested removal path.
This procedure covers the HTTP controller fields and authentication behavior checked against Mihomo v1.19.32, alongside the official general configuration. It does not claim an author end-to-end deployment or compatibility test of every dashboard and wrapper. Record your actual core, wrapper, operating system, and dashboard source before changing them.[1][2]
The self-hosting operations framework helps assign ownership of credentials, updates, and retirement. Administrative access can expose connection information or change routing behavior, while an open traffic proxy creates a different misuse risk. Protect and verify each interface independently rather than assuming one password secures both.
Work only on an instance you administer. Keep a private copy of the effective configuration and a recovery channel outside the controller. If this is a shared host, agree on the maintenance window and identify other consumers before rotating credentials. Do not stop or modify your everyday proxy, VPN client or an unrelated proxy to free a port.
Inventory the process actually running the core, its loaded profile, every controller listener, the proxy ports, dashboard files, and any forwarding service. On Linux, inspect listeners locally with ss -lntup; on another platform use its native listener inspector. Correlate the address and port with the owner rather than killing an unexpected process.
The mixed-port, SOCKS, and HTTP proxy listeners carry application traffic. external-controller serves the management API; secret authenticates its protected HTTP routes. The proxy authentication setting has a separate role. Changing allow-lan is therefore not a complete audit of controller exposure.[1]
Record IPv4, IPv6, containers, forwarded ports, and reverse proxies. A controller bound to loopback inside a container can still be reached through a separately configured intermediary; conversely, loopback on a workstation is not the server's loopback. Check the actual path from each authorized administrative location.
Treat a subscription URL as a credential-bearing input, following the subscription handling boundaries. Do not paste a full profile or Secret into a public issue, online YAML editor, remote dashboard you have not assessed, or screenshots. A short diagnostic should describe the listener and rejection result without exposing the credential.
For local administration, start with the existing instance's controller bound to loopback and a newly generated random Secret. The example below shows only the fields relevant to this change; it is not a complete proxy configuration. Replace the placeholder before loading it, and preserve unrelated routing and proxy credentials.[1][3]
external-controller: 127.0.0.1:9090
secret: 'REPLACE_WITH_A_UNIQUE_RANDOM_SECRET'
Generate the value locally with a trusted password manager or operating-system random source. Use a unique, high-entropy value; the placeholder is publicly known and cannot serve as a credential. Store it in the private configuration, restrict file permissions, and avoid command-line arguments, URLs, shell history, or shared logs that disclose it.
Remove unused alternative controller listeners from the intended configuration instead of assuming this field overrides all of them. Check wrapper-generated overrides and verify the effective listener after applying the change. Validate the configuration with the selected version's documented checker before reloading through the instance's normal owner.
The official documentation states that external-controller-unix and external-controller-pipe do not validate the Secret. If you enable them, access is governed by the operating system's socket path permissions or Named Pipe access controls. Their security cannot be established with the HTTP rejection test below.[1]
Leave these interfaces disabled if you do not need them. When they are necessary, put the socket in a private parent directory, inspect its actual owner and permissions, and restrict pipe access to intended accounts. Recheck after restart because creation permissions and service identities can differ from an existing file's permissions.
Do not enable the controller's optional DoH endpoint as part of API hardening: the general reference also identifies that endpoint as not checking the API Secret. DNS service exposure needs a separate scope and policy. Keeping unused services absent reduces the number of independent access boundaries you must verify.[1]
A dashboard is code running in your browser with access to an administrative credential. Obtain its files from a traceable project release, record the version, and avoid unreviewed third-party hosting or automatic updates during this change. A familiar appearance does not establish source integrity, and local hosting does not remove the need to trust the files.
If you serve a dashboard through external-ui, point it to the intended local directory and verify what is actually served. The general reference separates UI file handling from the controller listener and Secret. Do not mistake the UI's login form for API-side authentication; a direct request to a protected route must also be rejected without credentials.[1]
Keep cross-origin permissions limited to the dashboard origins actually needed. Avoid a wildcard origin or enabling private-network access merely to silence a browser error. CORS shapes browser access; it does not authenticate command-line clients or replace a Secret. If your wrapper requires additional origins, document why and test the resulting effective configuration.
For occasional remote administration, a narrowly bound SSH local forward is one controlled option. Use a verified host key and the existing authorized server account; keep the core's HTTP listener on server loopback. This example deliberately uses a different local port and must be run from the administrator's workstation, with the hostname and account replaced.
ssh -o StrictHostKeyChecking=yes -o ExitOnForwardFailure=yes \
-N -L 127.0.0.1:19090:127.0.0.1:9090 admin@controller-host.example
The SSH channel encrypts the remote leg; HTTP on the local loopback endpoint still requires the API Secret. Do not change this forward to a wildcard bind. Close the SSH session when finished and verify port 19090 is no longer listening locally. If the host key cannot be verified, stop rather than accepting it blindly.
An HTTPS entry is another design, requiring a valid certificate, certificate verification by the client, authenticated access, and restricted exposure. Account for any direct HTTP listener that could bypass it. Never use an insecure certificate option or a public raw HTTP controller as a workaround for TLS errors. Do not add WAN forwarding just to make the dashboard reachable.
Perform these checks against your own protected /configs endpoint. The first request has no credential and the second uses an intentionally incorrect value; neither should return the protected configuration. The example assumes the local listener at 9090; for the SSH forward substitute port 19090.[2]
curl --noproxy '*' --max-time 5 --silent --show-error \
--output /dev/null --write-out '%{http_code}\n' \
http://127.0.0.1:9090/configs
curl --noproxy '*' --max-time 5 --silent --show-error \
--header 'Authorization: Bearer intentionally-wrong-secret' \
--output /dev/null --write-out '%{http_code}\n' \
http://127.0.0.1:9090/configs
For v1.19.32, the expected authentication failure is HTTP 401. A timeout or connection refusal proves no successful authenticated access, but does not prove the Secret is being checked. Test a valid credential through the trusted local dashboard or a private curl configuration file; it should reach the protected endpoint successfully. Never put the real Secret into the published command or a query string.[2]
| Check method | Expected observation | Failure action |
|---|---|---|
| Inspect actual listening sockets | Only intended controller addresses and ports exist | Remove unexpected listener or forwarding owner |
| Request protected route anonymously | HTTP 401, no protected data | Stop external access and fix authentication |
| Request with wrong Secret | HTTP 401 | Check the effective profile and intermediary |
| Request with current valid Secret | Protected route succeeds | Check credential loading and route ownership |
| Attempt access from an excluded authorized test host | No controller access through unintended paths | Close forwarding or firewall exposure |
| Close remote entry and repeat its connection | Remote entry is unavailable | Find the remaining forwarding service |
These are reader acceptance conditions, not recorded author results. Check the direct core and any intermediary separately: a reverse proxy's own rejection could conceal an unauthenticated backend. Also confirm the dashboard can perform only the administration you intended and that its credential is not stored in a shared browser profile.
Create a new Secret, update the private effective configuration through its owner, and reload using the normal lifecycle. Update each trusted administrative client, then repeat the positive and negative checks. A new credential working is insufficient: a fresh request using the old credential must be rejected.
Terminate old dashboard sessions and administrative connections where the selected lifecycle requires it; an already established stream is not the same as a new authenticated request. Remove stale tokens from browser storage, private client files, and forwarding configuration. If the old credential was exposed, do not restore it from a backup to solve a client error.
For rollback after a configuration mistake, restore the saved listener settings while retaining a non-compromised Secret. Close the test SSH forward, remove only test-created ingress rules, and inspect listeners again. Recheck ordinary proxy operation and independent management access; do not disable the firewall globally to restore a dashboard.
For router-managed Mihomo, the OpenClash deployment and recovery guide explains that the plugin may regenerate core settings. If owning dashboards, credentials, and server maintenance is more than your task requires, compare self-hosted and managed service responsibilities before retaining that management plane.
That setting concerns proxy traffic access, while the controller has its own listener and authentication. Inspect the effective controller addresses and test a protected API route separately.
Keep authentication on the loopback HTTP controller. Local processes and browser code are not automatically trustworthy, and a later forwarding service can widen the listener's effective exposure.
A UI can show a password prompt without proving backend enforcement. Verify that direct anonymous and wrong-credential requests to a protected route are rejected by the core.
The documented Unix socket and Named Pipe interfaces do not validate the API Secret. Restrict operating-system access or leave them disabled; HTTP tests cannot establish their permissions.
CORS limits particular browser interactions, not all HTTP clients. Retain API authentication, narrow listeners, and restricted remote entry even when the allowed browser origins are explicit.
A verified SSH channel protects the remote transport but does not remove the HTTP API's authentication requirement. Keep local binding narrow and remove the forward after administration.
A new credential must work and the old credential must fail on a fresh protected request. Also retire existing administrative sessions and remove stale stored credentials through the relevant client lifecycle.
Disclaimer: These are documented management controls and reader checks, not claimed author test results. Make changes only on authorized systems and preserve a recovery channel that does not depend on the API.
Sources:
Sources checked 11 October 2026.
Related articles:
Sign up to experience all premium features at no cost.
*Available only to new users. Each user is limited to one trial.





