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.doUpdatehttps://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 SubscriptionBeanhttps://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 DataStorehttps://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.mdhttps://github.com/bannedbook/fanqiang/blob/master/macos/V2rayX.md】:

  1. Open V2RayX → click the menu icon → Configure…Advanced
  2. Switch to the Subscription tab, click the + button
  3. Double-click "enter your subscription link here", paste your URL, click outside to confirm
  4. 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 in UniversalFmt, store in SubscriptionBean
  • Auto-update behavior is controlled by DataStore.subscriptionAutoUpdate and subscriptionAutoUpdateDelay
  • The macOS V2RayX client provides equivalent functionality through a GUI subscription panel
  • Traffic quota data from Subscription-Userinfo headers 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →