First, identify which layer is failing
A browser may open websites while curl, git, package managers, or development tools fail in the terminal. That usually does not mean the Clash core has stopped working. More often, the two types of applications use different proxy entry points: a browser may read the operating system proxy or be controlled separately by an extension, while terminal programs often read only their own settings, proxy environment variables, or command-line arguments.
Break the path into four layers: whether the client is managing the core correctly, whether the core is listening on a local port, whether the target program sends requests to that port, and whether DNS and rules handle traffic correctly after it enters the core. Verify one layer at a time to define the fault boundary.
| Symptom | Check first | Common cause |
|---|---|---|
| Browser works, curl fails | Terminal proxy environment variables | curl is not reading the system proxy, or the variable points to an old port |
| Both browser and terminal fail | Core status and listening port | The core is not running, the configuration failed to load, or the port is already in use |
| Domain fails, IP request works | DNS path | Local resolution fails, the application bypasses Clash DNS, or fake-ip is incompatible |
| Only Git or a package manager fails | Application-specific settings | The application overrides environment variables or still has an old proxy address saved |
| Terminal works after enabling TUN | Original system proxy path | The program did not previously read the system proxy; TUN takes over at the network layer |
Step 1: Confirm that the core is running and listening on the right port
Read the actual port from the client interface
Do not assume the port is always 7890. Many Clash or mihomo configurations use mixed-port: 7890, but they may instead define port: 7890 and socks-port: 7891 separately. The client may also switch to another value when a port conflict occurs. Check the actual HTTP, SOCKS5, and Mixed Port values under “Settings” → “Preferences” or “Settings” → “Port Settings” in the current client. Menu names vary by client version, so follow what the interface shows.
A typical configuration includes the listening settings below. Use them only to identify the fields; troubleshooting must be based on the currently loaded configuration and runtime state.
mixed-port: 7890
allow-lan: false
mode: rule
log-level: info
mixed-port accepts both HTTP and SOCKS5 connections on the same port. With separate ports, an HTTP client should connect to port, while a SOCKS5 client should connect to socks-port. Using the wrong protocol and port commonly causes an immediate disconnect, an empty response, or a proxy handshake error.
Check listening status locally
Windows PowerShell can check whether a specific port is listening:
Get-NetTCPConnection -State Listen |
Where-Object LocalPort -In 7890,7891 |
Select-Object LocalAddress,LocalPort,OwningProcess
On macOS and Linux, use:
lsof -nP -iTCP:7890 -sTCP:LISTEN
lsof -nP -iTCP:7891 -sTCP:LISTEN
If ss is available on your system, you can also run:
ss -lntp | grep -E ':(7890|7891)\b'
Seeing 127.0.0.1:7890 means the port accepts connections only from the local machine, which is sufficient when the terminal and Clash run on the same computer. If the command runs inside WSL, a container, a virtual machine, or on another device on the local network, 127.0.0.1 in that environment refers to itself, not the host. You then need to identify the host address, virtual network boundary, and allow-lan setting. Do not simply expose the listening address to the entire network and stop investigating.
Step 2: Bypass system settings and test the proxy port directly
Once the port is confirmed to be listening, send a request through the proxy with an explicit argument. This separates “whether the core and proxy node work” from “whether the operating system distributes proxy settings correctly.”
Test the HTTP proxy with curl
curl -v --connect-timeout 10 \
-x http://127.0.0.1:7890 \
https://example.com/
In Windows PowerShell, if curl is an alias for Invoke-WebRequest, call curl.exe explicitly:
curl.exe -v --connect-timeout 10 ^
-x http://127.0.0.1:7890 ^
https://example.com/
If an explicit proxy request succeeds while a request without -x fails, the core, listening port, and current policy are likely working; the problem is that the terminal program is not using the proxy. If the explicit request also fails, check the Clash log at the same time: no connection record usually means the request never reached that port. A connection record followed by a REJECT, node timeout, or TLS error means you should continue with the rules, proxy groups, and upstream connection.
Test SOCKS5 and distinguish local from proxy-side DNS resolution
curl -v --connect-timeout 10 \
--proxy socks5h://127.0.0.1:7891 \
https://example.com/
In socks5h, the h means the proxy resolves the domain. With socks5, the domain is often resolved locally first. If socks5h succeeds but socks5 fails, shift the investigation from the proxy port to local DNS. This comparison is more informative than switching nodes immediately.
Choose a test target that is stable and matches the expected rules. If a domain is set to DIRECT, the request may bypass the selected proxy node; if it is set to REJECT, failure is the configured result. Rules are evaluated in order, and the first match determines the route, so verify the actual matched rule and policy in the log.
Step 3: Set the correct proxy variables for the terminal
Current shell on macOS and Linux
For a local Clash HTTP or Mixed Port, set the variables in the current terminal session:
export HTTP_PROXY=http://127.0.0.1:7890
export HTTPS_PROXY=http://127.0.0.1:7890
export ALL_PROXY=socks5h://127.0.0.1:7891
export NO_PROXY=localhost,127.0.0.1,::1
Some programs recognize only lowercase variables, while others prefer uppercase names. To eliminate compatibility differences, you can set both pairs, but make sure their values match:
export http_proxy="$HTTP_PROXY"
export https_proxy="$HTTPS_PROXY"
export all_proxy="$ALL_PROXY"
export no_proxy="$NO_PROXY"
These commands affect only the current shell and child processes started afterward. They do not automatically change editors, terminal tabs, or background services that are already running. For persistence, add them to ~/.zshrc, ~/.bashrc, or the appropriate startup file for your shell, then open a new terminal to verify the change. Do not keep different ports in multiple startup files.
Current Windows PowerShell session
$env:HTTP_PROXY = "http://127.0.0.1:7890"
$env:HTTPS_PROXY = "http://127.0.0.1:7890"
$env:ALL_PROXY = "socks5://127.0.0.1:7891"
$env:NO_PROXY = "localhost,127.0.0.1,::1"
Variables set with $env: apply only to the current PowerShell process and its child processes. User environment variables written through system settings are not refreshed in terminals that are already running; close and reopen them. Check the actual values with:
Get-ChildItem Env: |
Where-Object Name -Match '^(HTTP|HTTPS|ALL|NO)_PROXY$'
Clear stale ports and incorrect variables
After the client changes ports, the terminal may still contain the old value. For example, Clash may have moved to 7897 while HTTPS_PROXY still points to 7890. On macOS and Linux, temporarily clear them with:
unset HTTP_PROXY HTTPS_PROXY ALL_PROXY NO_PROXY
unset http_proxy https_proxy all_proxy no_proxy
In the current PowerShell session, use:
Remove-Item Env:HTTP_PROXY -ErrorAction SilentlyContinue
Remove-Item Env:HTTPS_PROXY -ErrorAction SilentlyContinue
Remove-Item Env:ALL_PROXY -ErrorAction SilentlyContinue
Remove-Item Env:NO_PROXY -ErrorAction SilentlyContinue
After clearing them, make one request without a proxy, then set the variables again using the actual port. This confirms that multiple sources are not overriding one another.
Step 4: Check independent settings in Git, package managers, and development tools
Correct environment variables do not guarantee that every tool will use them. Git, npm, Python toolchains, Java build tools, editors, and container runtimes may all store their own proxy settings. Check where each setting comes from before deciding whether to remove or edit it, rather than writing values to every configuration layer at once.
Git global and repository settings
git config --show-origin --get-regexp 'http\..*proxy|https\..*proxy'
--show-origin displays whether a setting comes from the system, user configuration, or the current repository. If you find an old port, update the global setting:
git config --global http.proxy http://127.0.0.1:7890
git config --global https.proxy http://127.0.0.1:7890
To make Git follow the environment variables instead, remove its own override:
git config --global --unset http.proxy
git config --global --unset https.proxy
Also check repository-level settings, because values in .git/config can override user configuration. When HTTPS requests use an HTTP proxy, the value of https.proxy usually still begins with http://. This means the client connects to an HTTP proxy first and then creates a TLS tunnel with CONNECT; it does not mean the destination website uses plain HTTP.
npm, pnpm, and Python tools
npm config get proxy
npm config get https-proxy
pnpm config get proxy
pnpm config get https-proxy
python -m pip config list -v
If the output shows an old address, correct it in the tool's user configuration. npm can remove its override with the following command so it reads the environment variables again:
npm config delete proxy
npm config delete https-proxy
Python's pip may read environment variables as well as configuration files in the user directory or virtual environment. Use python -m pip instead of entering pip alone to verify the tool associated with the current Python interpreter.
Integrated terminals and remote development in editors
An editor launched from a desktop icon may already be running before you change the terminal proxy variables. Its integrated terminal inherits the environment from when the editor process started, so fully quit the editor and reopen it. Remote SSH sessions, development containers, and WSL are separate environments: the host's system proxy does not automatically become the remote host's proxy, and the host's 127.0.0.1:7890 is not reachable from the remote host.
Step 5: Isolate DNS, rule mode, and TUN differences
Compare DNS paths with a domain and an IP
When the terminal reports Could not resolve host, Name or service not known, or getaddrinfo failed, the connection has not even reached the destination site. Start with a system resolution test:
nslookup example.com
curl -v https://example.com/
Then compare it with a request using socks5h. If proxy-side resolution succeeds while the regular request fails, check the operating system's DNS path, VPN or network extension resolvers, and whether the application specifies its own DNS. mihomo's fake-ip mode returns reserved addresses and maps domains inside the core, so the traffic must actually return to the core. Programs that bypass the system network stack or force a custom DNS resolver may behave differently.
no-resolve applies only to IP-based rules that include the parameter. It means not to proactively resolve a domain for that matching IP rule; it does not disable all Clash or mihomo DNS. When you see IP-CIDR,...,no-resolve in a configuration, do not attribute every domain-resolution failure to this parameter.
Rule logs reveal more than mode switching
In Rule mode, the core matches requests in the order defined by the configuration. Logs usually show the destination domain or IP, the matched rule, the policy group, and the final outbound route. If a browser and terminal hit the same domain but match different rules, common causes include the terminal resolving the domain to an IP first, the applications connecting to different subdomains, different IPv4 and IPv6 results, or the two requests entering different core ports.
Temporarily switching to Global is useful only for narrowing the scope: if Global succeeds but Rule fails, inspect the rules and policy groups; if both modes fail, continue checking the port, DNS, node, and network path. Restore the original mode after testing. Do not treat permanent Global mode as a fix for a rule problem.
What TUN can solve—and what it cannot replace
System proxies mainly serve applications that honor the operating system's proxy settings. TUN mode uses a virtual network interface to capture a broader range of IP traffic, so terminal programs without HTTP or SOCKS5 support may reconnect when TUN is enabled. This shows that the original application-layer proxy path did not cover the program; it does not mean the system proxy switch itself is broken.
Before enabling TUN, check the required permissions under “Settings” → “TUN Mode” or the corresponding network settings in the client. Windows may require service mode or administrator privileges; macOS may require approval for a VPN configuration or network extension; Linux commonly involves network-management permissions, routes, and DNS settings. Clients based on mihomo expose different implementation paths, so follow the current interface and logs.
Narrow the problem from the results instead of repeatedly toggling switches
After completing these checks, use the sequence below to reach a reproducible conclusion. Record the commands, ports, and log timestamps at each step so you can retest quickly after changing clients or configurations.
- Confirm in the client that the current core is running, and note the actual HTTP, SOCKS5, or Mixed Port.
- Use
lsof,ss, orGet-NetTCPConnectionto confirm that the expected process is actually listening on that port. - Use
curl -xorcurl --proxy socks5h://to connect to the proxy explicitly, and check whether the corresponding request appears in the Clash log. - After the explicit proxy works, check
HTTP_PROXY,HTTPS_PROXY,ALL_PROXY, and their uppercase and lowercase variants. - When only one tool fails, inspect the configuration sources used by Git, npm, pip, the editor, or the build tool.
- When domains fail but proxy-side resolution succeeds, investigate system DNS, fake-ip return traffic, and application-defined DNS.
- When only TUN works, confirm whether the target program supports the system proxy and check the network boundaries of the terminal, container, or remote environment.
A typical result looks like this: Clash's Mixed Port is actually 7897, and the browser works because it reads the system proxy just written by the client. The terminal's HTTPS_PROXY still points to 127.0.0.1:7890, while Git also stores the same old port in the user configuration. First change the terminal variable to 7897, then remove or update Git's override, and finally retest with an explicit proxy and a regular Git request. There is no need to keep switching between Rule, Global, and TUN.
Another common result is that the HTTP proxy test fails while socks5h://127.0.0.1:7891 succeeds. First confirm that an HTTP port is actually configured; do not use the SOCKS5 port as an HTTP port. If regular SOCKS5 fails while socks5h succeeds, continue investigating local DNS. This layered comparison narrows “Clash is not working” to a specific port, application setting, resolution, or rule problem.