How to Use Subscription URLs for Proxy Servers in the Fanqiang Project
Subscription URLs provide a standardized way to distribute proxy node configurations to client applications, allowing automatic fetching, parsing, and storage of proxy servers.
In the bannedbook/fanqiang repository, subscription URLs power both the Android client (Sagernet-based) and the macOS GUI client (V2RayX). This guide explains the complete workflow—from URL format to database storage—with direct references to the source code implementation.
What Is a Subscription URL?
A subscription URL is a plain-text HTTP endpoint that returns a list of proxy node configurations. Common formats include Vmess, Shadowsocks, Trojan, and other proxy protocols.
Example URL:
https://example.com/subscription
The response body contains newline-separated proxy URIs:
vmess://eyJ2IjogIjIiLCAicHMiOiAiU2VydmVyIDEiLCAiYWRkIjogIjEuMi4zLjQiLCAicG9ydCI6ICI0NDMiLCAiaWQiOiAiLi4uIn0=
ss://YWVzLTI1Ni1nY206password@server:port
Subscription URL Workflow in the Android Client
The Android implementation follows a five-step pipeline defined in RawUpdater.doUpdate【https://github.com/bannedbook/fanqiang/blob/master/fqnews2/app/src/main/java/io/nekohasekai/sagernet/group/RawUpdater.kt】.
1. HTTP Fetch with Custom User-Agent
The client issues an HTTP GET request. The User-Agent can be customized through DataStore settings.
// From RawUpdater.kt - simplified fetch logic
val response = HttpURLConnection(url).apply {
setRequestProperty("User-Agent", userAgent ?: "Sagernet/*")
}.inputStream.use { it.readBytes() }
2. Protocol-Specific Parsing
The response body passes through format-specific parsers. For Vmess and universal formats, UniversalFmt handles the conversion【https://github.com/bannedbook/fanqiang/blob/master/fqnews2/app/src/main/java/io/nekohasekai/sagernet/fmt/UniversalFmt.kt】.
3. Database Storage
Parsed proxies are stored in SubscriptionBean【https://github.com/bannedbook/fanqiang/blob/master/fqnews2/app/src/main/java/io/nekohasekai/sagernet/database/SubscriptionBean.java】 and linked to a ProxyGroup.
// SubscriptionBean.java core fields
@Entity(tableName = "subscription_bean")
public class SubscriptionBean {
public long id;
public String name;
public String url; // the subscription URL
public String subscriptionUserinfo; // traffic quota data
public long lastUpdated;
}
4. Optional Cleanup Steps
- Force resolve: Converts hostnames to IP addresses
- Deduplication: Removes duplicate server entries before storage
5. Routing Engine Integration
The proxy list becomes available to the routing engine and updates the UI's selectable servers list.
Configuring Subscription URLs Programmatically
Manual Subscription Update
Trigger an immediate update for a specific group:
// Assume proxyGroup already has a SubscriptionBean attached
val subscription = proxyGroup.subscription!!
RawUpdater.doUpdate(proxyGroup, subscription, null, byUser = true)
Persistent Subscription Settings
Store the subscription URL and auto-update preferences in DataStore【https://github.com/bannedbook/fanqiang/blob/master/fqnews2/app/src/main/java/io/nekohasekai/sagernet/database/DataStore.kt】:
val ds = DataStore(context)
ds.subscriptionLink = "https://example.com/subscription"
ds.subscriptionAutoUpdate = true // enable background updates
ds.subscriptionAutoUpdateDelay = 360 // check every 360 seconds
The subscriptionAutoUpdate flag controls whether the app periodically refreshes the subscription.
Subscription URL Handling in macOS (V2RayX)
The macOS client exposes subscription URLs through a graphical interface. According to macos/V2rayX.md【https://github.com/bannedbook/fanqiang/blob/master/macos/V2rayX.md】:
- Open V2RayX → click the menu icon → Configure… → Advanced
- Switch to the Subscription tab, click the + button
- Double-click "enter your subscription link here", paste your URL, click outside to confirm
- Click Finish — V2RayX fetches immediately and populates the server table
No code changes required; the GUI handles fetching, parsing, and storage automatically.
Testing Subscription URLs Manually
Verify a subscription endpoint with curl:
curl -A "FanQiang/Android" https://example.com/subscription
Expected output: newline-separated proxy URIs. Check for:
- Valid base64 encoding (vmess://, vless:// prefixes)
- Complete server address, port, and authentication credentials
Subscription-Userinfo Header Support
If the HTTP response includes a Subscription-Userinfo header, the Android client saves it to SubscriptionBean.subscriptionUserinfo. This typically contains traffic quota information:
Subscription-Userinfo: upload=1234567; download=7654321; total=1073741824; expire=1893456000
The macOS client displays comparable data in its subscription panel when available.
Key Implementation Files
| File | Purpose |
|---|---|
fqnews2/app/src/main/java/io/nekohasekai/sagernet/group/RawUpdater.kt |
HTTP fetch orchestration and update workflow |
fqnews2/app/src/main/java/io/nekohasekai/sagernet/database/SubscriptionBean.java |
Persistent storage model for subscription data |
fqnews2/app/src/main/java/io/nekohasekai/sagernet/database/DataStore.kt |
Global settings: URL, auto-update flags, delays |
fqnews2/app/src/main/java/io/nekohasekai/sagernet/fmt/UniversalFmt.kt |
Protocol parser for vmess and universal formats |
macos/V2rayX.md |
End-user documentation for macOS subscription setup |
Summary
- Subscription URLs are HTTP endpoints returning proxy node lists in vmess, Shadowsocks, or similar formats
- The Android client implements a complete pipeline: fetch in
RawUpdater.doUpdate, parse inUniversalFmt, store inSubscriptionBean - Auto-update behavior is controlled by
DataStore.subscriptionAutoUpdateandsubscriptionAutoUpdateDelay - The macOS V2RayX client provides equivalent functionality through a GUI subscription panel
- Traffic quota data from
Subscription-Userinfoheaders persists for user visibility
Frequently Asked Questions
How do I add a subscription URL in the Android fanqiang app?
Set DataStore.subscriptionLink to your URL and enable subscriptionAutoUpdate. Trigger manual updates by calling RawUpdater.doUpdate(proxyGroup, subscription, null, byUser = true) with your target group.
What format should my subscription URL return?
Plain text with newline-separated proxy URIs. Supported protocols include vmess://, vless://, ss://, ssr://, and trojan://. The UniversalFmt parser in UniversalFmt.kt handles the conversion.
How often does the Android client refresh subscriptions?
Controlled by DataStore.subscriptionAutoUpdateDelay, measured in seconds. Default behavior checks periodically when subscriptionAutoUpdate is true. Manual updates bypass this interval.
Does the macOS client support the same subscription features?
Yes. V2RayX fetches and parses identically, storing results in its local server list. The interface exposes URL input and immediate refresh through the Subscription tab in Advanced settings.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →