Skip to content

fix(sub): serve a copy-only page when a subscription URL is opened in a browser - #6183

Open
n0ctal wants to merge 2 commits into
MHSanaei:mainfrom
n0ctal:upstream-browser-subscription-copy-page
Open

fix(sub): serve a copy-only page when a subscription URL is opened in a browser#6183
n0ctal wants to merge 2 commits into
MHSanaei:mainfrom
n0ctal:upstream-browser-subscription-copy-page

Conversation

@n0ctal

@n0ctal n0ctal commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Summary

Serve a copy-only page, instead of the full subscription page, when a subscription URL is opened by a browser, and widen browser detection beyond the Accept header.

Why

maybeServeSubPage renders the normal subscription page for any request that looks like a browser. That page embeds the share links in its page data — the actual configs, inline in the HTML.

Subscription URLs travel. They get pasted into chats, forwarded to a second device, opened to "check if it still works". Anything that fetches the URL with an HTML-ish Accept header renders the page and receives every config on it: a messenger unfurling a link preview, a corporate proxy prefetching, a browser extension, a shared-screen moment. None of that requires the recipient to intend anything — the configs are simply in the response body.

Detection was also narrower than the traffic it has to classify. It keyed on Accept: text/html, ?html=1 and ?view=html only, so a browser navigation that did not advertise HTML fell through to the raw subscription body.

Scope

  • maybeServeSubPage still validates the subscription, but renders serveSubscriptionCopyPage — a page that lets the user copy the subscription URL without embedding the config payload.
  • New isBrowserSubscriptionRequest adds two further signals:
    • Sec-Fetch-Dest: document / Sec-Fetch-Mode: navigate, which browsers send on real navigations regardless of Accept.
    • a Mozilla/ User-Agent check, with an explicit allowlist for VPN clients that also send a Mozilla/ prefix (clash, mihomo, sing-box, v2ray, xray, hiddify, nekobox, shadowrocket, streisand, v2box and others) so importing clients keep receiving the raw body.
  • No change to what a subscription client receives.

Validation

  • go build ./internal/... clean.
  • go test ./internal/sub/ green on main at ece16559 with this applied, including the new browser-detection cases.
  • Cherry-picks onto current main without conflict.

Risk

Low for clients, moderate for anyone who relied on the browser page to read configs directly — that is the behaviour being removed on purpose. The client allowlist is the one place to review: a client not on it that sends Mozilla/ and does not identify itself would now get the copy page instead of the body. Happy to extend the list with any client you know of.

n0ctal added 2 commits August 8, 2026 06:51
Browser navigation to /sub previously rendered the normal subscription page, which exposed subscription material in page data or raw base64 depending on request headers. Keep VPN clients on the raw subscription body, but classify browser document requests and return a neutral static copy-only HTML page with no embedded share links or page data.

This preserves the C1 LimitIP parser fix in the same master candidate while avoiding a DE rollback of the browser subscription UX.
Only implicit browser navigation is downgraded to the copy-only page. An
operator who appends html=1 or view=html already holds the URL, so the
themed subscription page keeps rendering for them and serveSubPage stays
in use.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant