Subscriptions and response rules
Response rules decide what the panel returns for every subscription request. A client app needs its configs, a browser deserves a nice page, and a stranger script walking through stolen links is best served a 404 and nothing else. The module shows the current state on one screen, applies ready-made rule sets and enables per-device subscription binding. Everything changes through the panel API, no configs are edited by hand.
How it works
Section titled “How it works”The panel evaluates every subscription request by its headers: which app sent it and from which device. A rule combines header conditions with a response type. Rules are checked top to bottom, the first match wins, and its response type goes to the client.
Device binding works alongside the rules. Happ and INCY send an x-hwid header with the device identifier. When the limit is on, a user’s subscription is served to at most N devices, the rest receive a polite message instead of configs. The list of occupied slots is visible in the user’s card in the panel, a lost device can be removed there to free its slot. Individual users can have their own limit or none at all, that is done in the panel.
Everything is stored in the panel, the same settings are available in its web UI in the subscriptions section. The module is a convenience wrapper: it changes values through the API, shows the result and keeps copies. If you use the panel’s external squads, keep in mind that response rules take priority over their settings.
What the panel can return
Section titled “What the panel can return”The response type in a rule decides what exactly the client receives. The module’s rule sets use some of the types, custom rules in the panel can be built from the full list.
| Response type | What the client receives |
|---|---|
| Xray JSON | The complete Xray config as one file, with routing rules. This is how Happ, INCY and v2rayN work |
| Base64 | The plain server list Base64-encoded. Every app understands it, a safe default choice |
| Mihomo, Clash, Stash, Sing-box | A config in the format of that client family |
| Browser page | The subscription HTML page with the server list and instructions |
| Block 403, 404, 451 | An empty refusal. For stranger requests 404 looks the most natural: as if nothing ever lived at this address |
| Socket drop | The panel silently drops the connection, without a status code |
-
Open item
5“Subscriptions: rules and page” of the main menu and pick “Subscriptions and response rules”:REMNAWAVE REVERSE-PROXY by eGames Version: 3.8.1 Wiki: https://wiki.egam.es/ 5.Subscriptions: rules and page 6.WARP Native 8.Xray Checker — subscription monitoring 0.Exit Select action (0-13):5
Subscriptions: rules and page 1.Subscriptions and response rules which config each client receives, the device limit, JSON and paths 2.Subscription page the Orion template and restoring the stock page 0.Exit Select action (0-2):1
-
The module shows the state right away: which rules are active, whether the device limit is on, what the base path serves. The actions are below:
Subscriptions and response rules Details in the wiki: https://wiki.egam.es/configuration/subscription-rules/ Response rules: not configured recent panel versions answer 403 to subscription requests until rules are set, apply a preset below ✗ HWID device limit: disabled JSON at the subscription base path: disabled Extra paths /json, /stash, /mihomo: open 1.Show rules in detail 2.Preset “Clients and browsers” browsers get the subscription page, every app gets Base64 configs 3.Preset “Strict: Happ and INCY + devices” configs go only to Happ/INCY with device binding and to known apps, everyone else gets 404 4.Allow everyone with a single rule 5.HWID device limit: enable 6.JSON at base path: enable 7.Extra paths /json, /stash, /mihomo: close 8.Restore saved rules 0.Exit Select action (0-8):2
-
The “Clients and browsers” preset suits most setups: the browser gets the subscription page, every app receives its configs as Base64, the format every client understands:
[?] Replace the current rules (0) with preset “Clients and browsers” of 2 rules? The current rules will be saved to a file. (y/n):y Current rules saved: rules-20261009-143210.json Preset applied Changes apply immediately. Apps pick them up on the next subscription update.
-
The state screen refreshes. Item 1 shows every rule with its conditions:
Response rules: 2 1. ✓ Browsers → browser page 2. ✓ Everyone else → Base64 ✗ HWID device limit: disabled Select action (0-8):1 ✓ 1. Browsers [AND → browser page] user-agent CONTAINS "Mozilla" (case-insensitive) ✓ 2. Everyone else [AND → Base64] no conditions, the rule matches every request Press any key to go back…
Strict preset
Section titled “Strict preset”The “Strict: Happ and INCY + devices” preset is built for selling subscriptions. It hands configs only to verified requests and returns 404 to everything else. The rule set looks like this:
| Who asks | Condition | What they receive |
|---|---|---|
| Happ, INCY and RenoarX | user-agent starts with Happ/, INCY/ or RenoarX/, a valid x-hwid is present |
The complete Xray JSON config |
| Clash clients | user-agent starts with FlClashX/, koala-clash/, prizrak-box/, rabbithole/, clash-mi/, Flowvy/ or ClodClash/, a valid x-hwid is present |
The Mihomo config for their Clash core |
| Sing-box clients | user-agent starts with Karing/, InHive/, Throne/ or DeskBox/, a valid x-hwid is present |
The Sing-box config |
| Known apps | user-agent starts with a name from the list: v2rayN, v2rayNG, v2RayTun, V2Box, Streisand, Shadowrocket, sing-box, Karing, FlClash, Flowvy, prizrak-box, Koala Clash, Clod Clash, RenoarX, Throne, DeskBox, sfa, sfi, sfm, sft, rabbithole |
The Base64 list |
| Browser | user-agent contains Mozilla |
The subscription page |
| Everyone else | no conditions | 404 |
Response rules: 6 1. ✓ Xray JSON clients with device → Xray JSON 2. ✓ Clash clients with device → Mihomo 3. ✓ Sing-box clients with device → Sing-box 4. ✓ Known apps → Base64 5. ✓ Browsers → browser page 6. ✓ Everyone else → 404
The strict preset makes sense together with the device limit: a stolen link then stays useless even with a faked app name, because the stranger device will not pass the limit. Clash and Sing-box clients, RenoarX, Happ and INCY report the device identifier to the panel: with the limit on they pass the check and receive the config in their own format.
Device limit
Section titled “Device limit”Enabled by item 5. The panel asks how many devices per user are allowed and shows a warning:
[?] Device limit per user (1-20, Enter will keep 3):3 Clients without the x-hwid header (v2rayN, v2rayNG, FlClash, Stash) will not be able to fetch the subscription. For such users the limit is disabled per user in the panel. [?] Enable the device limit: up to 3 per user? (y/n):y Device limit enabled: up to 3 per user Changes apply immediately. Apps pick them up on the next subscription update.
What the panel knows about a device:
| Header | What it carries | Example |
|---|---|---|
x-hwid |
The device identifier, required for the limit | UE42LJXu4DbiCaBv |
x-device-os |
The device system | iOS or Android |
x-ver-os |
The system version | 18.3 |
x-device-model |
The device model | iPhone 14 Pro Max |
Happ, INCY, RenoarX, DeskBox, v2RayTun, Flowvy, Clod Clash and the Clash clients send these headers on their own. Throne, Shadowrocket, Clash Mi and Karing support the binding too, but send the header only after it is enabled in the app settings. Left without the header are v2rayN, v2rayNG, FlClash, Stash and the official sing-box apps: with the limit on, their users will not receive the subscription. Such a user gets the limit disabled individually in their panel card, the restriction keeps working for everyone else.
The limit counts distinct devices. When a user changes phones and the old one holds a slot, the extra device is removed from the card list and the new one passes right away.
Restoring
Section titled “Restoring”Before every rule replacement the module stores the current set in /usr/local/remnawave_reverse/subscription-rules/ with a timestamp. Item 8 lists the copies, the picked one returns to the panel, and the state at restore time is saved as well. The ten latest copies are kept.
Restoring rules from a copy 1.2026-10-09 14:32:10 · rules: 4 2.2026-10-09 14:20:03 · rules: 2 0.Exit Select action (0-2):1 [?] Restore rules from rules-20261009-143210.json (4 rules)? (y/n):y Current rules saved: rules-20261009-144502.json Rules restored
