[Bug]: Subscription: `?serverDescription=` in client external links (kind=link) gets percent-encoded, Happ subtitle is lost
Describe the bug
Happ shows a subtitle under the server name when the link fragment is #<name>?serverDescription=<base64> (documented by Happ; 3x-ui already keeps it literal for inbound/host links since #6488).
For client external links (POST /panel/api/clients/{email}/externalLinks, kind: "link") the fragment is stored correctly, but the generated subscription re-encodes it: ? becomes %3F and / inside the base64 becomes %2F. Happ no longer recognises the parameter and the subtitle disappears. The same link imported into Happ manually (not via the subscription) shows the subtitle correctly.
#6488 added strings.Cut handling of ?serverDescription= in appendQueryAndFragment for links built from inbounds/hosts. External link rows appear to go through a different path that escapes the whole fragment. Applying the same split there (escape only the name, keep ?serverDescription=<base64> literal, validate the tail for #, &, spaces and newlines) would fix it.
External links are the main use case for operators who aggregate servers from several sources, so hosts-level serverDescription does not cover them.
How to reproduce the problem
- Add an external link to a client via
POST /panel/api/clients/{email}/externalLinks(or the client's "Links" tab):{"externalLinks":[{"kind":"link","enable":true,"value":"vless://[email protected]:443?type=tcp&security=reality&pbk=XXX&fp=chrome&sni=example.org&sid=00&flow=xtls-rprx-vision&encryption=none# Warsaw ⚡️?serverDescription=0JTQu9GPIExURSAo0LHQtdC70YvQtSDRgdC/0LjRgdC60Lgp"}]} GET /panel/api/clients/get/{email}— the value is stored unchanged.- Fetch that client's subscription URL and base64-decode it.
- Observe the fragment of the external link line:
?serverDescription=is escaped to%3FserverDescription=. - Add the subscription to Happ — no subtitle. Import the same
vless://link into Happ directly — the subtitle is shown.
Expected behavior
The name part may be percent-encoded, but ?serverDescription= and its base64 value stay literal:
#%F0%9F%87%B5%F0%9F%87%B1%20Warsaw%20%E2%9A%A1%EF%B8%8F?serverDescription=0JTQu9GPIExURSAo0LHQtdC70YvQtSDRgdC/0LjRgdC60LgpActual behavior
#%F0%9F%87%B5%F0%9F%87%B1%20Warsaw%20%E2%9A%A1%EF%B8%8F%3FserverDescription=0JTQu9GPIExURSAo0LHQtdC70YvQtSDRgdC%2F0LjRgdC60LgpHapp does not show the subtitle.
Relevant logs
Screenshots
3x-ui version
3.7.0
Xray-core version
26.7.28
How did you install 3x-ui?
install.sh script
Operating system
Ubuntu 24.04
Which parts of the panel are affected?
Subscription (share links / Clash / JSON)
Browser (only if it is a UI bug)
No response
Is the panel behind a reverse proxy or CDN?
None
Before submitting
- I searched existing issues and this bug has not been reported.
- I am running the latest released version of 3x-ui (or have verified the bug still exists on it).
- This bug report is written in English.
- I have redacted any sensitive data (tokens, real domains, client UUIDs).
Source: MHSanaei/3x-ui