mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-09-30 11:12:08 +03:00
1d85ef138e
* fix(sub): prevent default profile page URL disclosure Add explicit none, builtin, and custom profile page modes. Preserve existing custom URLs and warn before exposing the built-in page. Cover mode selection, legacy settings, and subscription response headers. * fix(subscription): add profile page link options and upgrade notes
152 lines
8.1 KiB
Plaintext
152 lines
8.1 KiB
Plaintext
---
|
|
title: Subscription
|
|
description: Run the 3x-ui subscription server — base64/JSON/Clash formats, ports and paths, TLS, response headers, and custom templates.
|
|
icon: Rss
|
|
---
|
|
|
|
A **subscription** is a single URL that returns all of a client's
|
|
configurations. Client apps refresh it periodically, so when you change an
|
|
inbound, clients pick up the change automatically. The subscription server runs
|
|
as a **separate** server from the panel.
|
|
|
|
## Enable and configure
|
|
|
|
The subscription server is **on by default** (`subEnable`). Configure it in the
|
|
panel's subscription settings:
|
|
|
|
| Setting | Default | Meaning |
|
|
| ------------- | ------- | --------------------------------------------------------------- |
|
|
| `subPort` | `2096` | Listen port (separate from the panel). |
|
|
| `subListen` | _(all)_ | Bind address. |
|
|
| `subPath` | _(random per panel)_ | Base path for raw subscription URLs. |
|
|
| `subDomain` | _(none)_| Public host; if set, the server only answers for that Host. |
|
|
| `subCertFile` / `subKeyFile` | _(none)_ | TLS cert + key — when set, the server serves **HTTPS**. |
|
|
| `subEncrypt` | `true` | Base64-encode the raw subscription body. |
|
|
| `subUpdates` | `12` | Suggested refresh interval (hours) sent to clients. |
|
|
|
|
A subscription URL looks like:
|
|
|
|
```text
|
|
https://<sub-host>:<sub-port>/<sub-path>/<sub-id>
|
|
```
|
|
|
|
where `<sub-id>` is the client's **Sub ID**.
|
|
|
|
The same Sub ID is served in several formats on different paths — the **Base64**
|
|
list at `subPath` and the **JSON** (Xray-json) config at the JSON path. Build the
|
|
URLs and preview both bodies here:
|
|
|
|
<SubscriptionBuilder />
|
|
|
|
## Output formats
|
|
|
|
The **format is chosen by path**, each with its own enable toggle:
|
|
|
|
| Format | Path | Enabled by | Output |
|
|
| ------------------------------ | ---------------- | ---------------- | --------------------------------------------------- |
|
|
| **Raw links** | `subPath` | always (if on) | A list of `vless://`, `vmess://`, … links (base64-encoded when `subEncrypt` is on). |
|
|
| **JSON** | `subJsonPath` | `subJsonEnable` | Full Xray client config(s). |
|
|
| **Clash / Mihomo** | `subClashPath` | `subClashEnable` | Full Mihomo-compatible YAML profile. |
|
|
| **Mihomo (explicit)** | `/mihomo/` | `subClashEnable` | Alias for the full `subClashPath` profile. |
|
|
| **Clash for Windows (legacy)** | `/clash-legacy/` | `subClashEnable` | YAML limited to proxy types, transports, and ciphers supported by the legacy Clash core. |
|
|
|
|
Only enabled inbounds using **VLESS, VMess, Trojan, Shadowsocks, WireGuard, AmneziaWG, MTProto, TUIC, or Hysteria2**
|
|
appear in a subscription, ordered by their sub-sort index (TUIC and AmneziaWG are included in raw links and Clash/Mihomo profiles, but omitted from JSON endpoints; MTProto is included in raw links). Requesting `subPath`
|
|
with an `Accept: text/html` header (or `?html=1`) returns a human-readable info
|
|
page instead of the raw body.
|
|
|
|
Use `/mihomo/<sub-id>` for Clash Verge Rev, Mihomo, and other maintained
|
|
Mihomo-based clients. Use `/clash-legacy/<sub-id>` only for the discontinued
|
|
Clash for Windows client. The legacy endpoint keeps compatible VMess, Trojan,
|
|
and Shadowsocks nodes and excludes VLESS, Hysteria2, Reality, XHTTP,
|
|
HTTPUpgrade, and Shadowsocks 2022. If no compatible node exists, it returns an
|
|
explicit `422` response instead of a YAML profile the client cannot import.
|
|
To avoid Mihomo-only syntax entering the legacy profile, this endpoint always
|
|
uses its minimal `PROXY` group and `MATCH,PROXY` rule and ignores custom Clash
|
|
routing settings.
|
|
|
|
If an administrator has already assigned `/mihomo/` or `/clash-legacy/` to a
|
|
different configurable subscription path, that existing path is preserved and
|
|
the conflicting alias is skipped with a warning at startup.
|
|
|
|
Automatic Clash format detection keeps the existing `(?i)(clash|mihomo)`
|
|
default matcher so existing subscription URLs continue returning YAML.
|
|
It does not distinguish legacy clients from Mihomo-based clients; Clash for
|
|
Windows users must use `/clash-legacy/<sub-id>` for a compatible profile.
|
|
|
|
### Base64 vs JSON
|
|
|
|
The **Base64** body is just the newline-joined share links, standard-base64
|
|
encoded (toggle with `subEncrypt`). The **JSON** body wraps each client in a
|
|
complete Xray client config — a fixed skeleton (local SOCKS/HTTP inbounds bound to 127.0.0.1, DNS,
|
|
routing, policy) plus a `proxy` outbound pointing at the inbound. 3x-ui emits a
|
|
**single config object for one client and an array for several**, uses the flat
|
|
outbound `settings` form (`address`/`port`/`id`, `level: 8`), and strips
|
|
`sockopt` from `streamSettings`.
|
|
|
|
## Response headers
|
|
|
|
Subscriptions return standard headers that compatible apps read:
|
|
|
|
- **`Subscription-Userinfo`** — `upload`, `download`, `total` (bytes; `total=0`
|
|
means unlimited) and `expire` (Unix seconds).
|
|
- **`Profile-Update-Interval`** — refresh interval in hours (`subUpdates`).
|
|
- **`Profile-Title`**, **`Support-Url`**, **`Profile-Web-Page-Url`**,
|
|
**`Announce`** — optional branding shown by some clients.
|
|
|
|
### Profile page links and upgrades
|
|
|
|
In **Subscription → Profile → Profile page**, choose `subProfileMode` for all
|
|
subscription clients:
|
|
|
|
- **No link** (`none`, default): omit `Profile-Web-Page-Url`.
|
|
- **Built-in subscription page** (`builtin`): link to the client's built-in page.
|
|
- **Custom website** (`custom`): use `subProfileUrl`; a blank URL omits the header.
|
|
|
|
**Upgrade note:** previously, an empty `subProfileUrl` automatically linked to
|
|
the built-in page. After upgrading, an unset mode with an empty or whitespace-only
|
|
URL becomes **No link**; an existing nonempty URL remains a **Custom website**.
|
|
To restore the built-in link, select **Built-in subscription page** above and
|
|
save the settings.
|
|
|
|
The built-in page exposes subscription URLs and node configurations, including
|
|
for Happ encrypted subscriptions. Enable it only if you intend to provide that
|
|
access.
|
|
|
|
### Optional month-end expiry display
|
|
|
|
Under **Subscription → Information**, **Month-end subscription expiry display**
|
|
(`subCalendarExpireInclusive`, default `false`) reports the last valid second
|
|
of the month in `Subscription-Userinfo` instead of the next month's midnight.
|
|
It applies only when every client contributing to the subscription has calendar
|
|
renewal day `1`, shares the same fixed expiry, and that expiry is exactly day `1`
|
|
at `00:00:00` in the configured panel timezone, immediately after the previous
|
|
month's last second. A later repeated midnight during a DST rollback is not
|
|
converted. Raw, JSON, Mihomo, and legacy
|
|
Clash subscriptions use the same conversion.
|
|
|
|
For example, the real cutoff `2030-10-01 00:00:00` is presented as
|
|
`2030-09-30 23:59:59`. The stored expiry, access cutoff, traffic accounting,
|
|
renewal schedule, remark expiry variables, and HTML/JSON info-page cutoff stay
|
|
unchanged. Arbitrary times, other renewal days, interval renewal, first-use
|
|
durations, unlimited expiries, mixed renewal modes, and different cutoffs are
|
|
not converted.
|
|
|
|
This is an opt-in compatibility tradeoff, not a change to expiry semantics by
|
|
default: apps receive a timestamp one second before the real cutoff and may
|
|
consider the subscription expired one second early. Apps format it in their own
|
|
timezone; matching the panel timezone is needed to display the same month-end
|
|
date. Cached subscription information changes only after the app refreshes it.
|
|
|
|
## Custom page templates
|
|
|
|
Point `subThemeDir` at a folder containing a custom info-page template to brand
|
|
the HTML subscription page. The per-client remark on each link is fully
|
|
templated — see [Share links → remark variables](/docs/config/share-links#remark-template-variables).
|
|
|
|
<Callout type="info">
|
|
Put the subscription server behind TLS (set `subCertFile`/`subKeyFile`, or a
|
|
[reverse proxy](/docs/operations/reverse-proxy)) so subscription contents
|
|
aren't exposed in transit.
|
|
</Callout>
|