A subscription URL is an access point, Base64 is an encoding method, a share link describes a single connection, and native JSON may contain a complete core configuration. This guide is for users migrating subscriptions, troubleshooting import failures, or preserving custom settings: identify the input, map the fields, then check that the connection, transport, and routing are intact in the client.
1. Distinguish URLs, encoding, and configuration data
“V2Ray subscription format” isn’t a single file standard. A typical subscription endpoint returns text over HTTPS. That text may contain multiple share links, or the entire list may be Base64-encoded. The subscription URL itself isn’t a node configuration: the client must request the URL and parse the response.
A filename doesn’t prove the content format either. A URL ending in .json may return an error page, while a URL with no extension may return valid JSON. Check the response first, then choose “Update subscription,” “Import from clipboard,” or “Import custom config.”
Provider-supported subscription
RecommendedIf you need to keep nodes up to date, use a subscription explicitly supported by your target client. The response is often a list of share links wrapped in Base64, but check the provider’s documentation and the actual response.
Best for: regular updates, importing on multiple devices, and changed node addresses
One or more share links
Links such as vmess:// and vless:// make it easy to move individual connections. Multiple links are usually listed one per line; the links themselves don’t provide automatic updates.
Best for: quick tests, migrating a single node, and checking connection fields
Native JSON config
A full config can describe inbounds, outbounds, DNS, and routing. JSON is only the syntax; its fields must match the target core and version. A config that parses isn’t necessarily runnable.
Best for: custom routing, custom DNS, and migrating a complete runtime setup
2. Identify and decode a Base64 subscription
A traditional link-list subscription is commonly created by placing each share link on its own line, converting the text to UTF-8 bytes, and then Base64-encoding the result. Decoding it should reveal recognizable protocol prefixes—not a complete core config.
This JavaScript demonstrates local string processing only. It accepts standard Base64 and the commonly used URL-safe alphabet. It removes whitespace from the encoded text, not characters inside share links. If the length is invalid or the decoded bytes are malformed, stop and inspect the input—don’t try repeated decoding to “fix” it.
function decodeSubscription(encoded) {
const text = encoded.trim()
.replace(/\s/g, "")
.replace(/-/g, "+")
.replace(/_/g, "/");
if (!/^[A-Za-z0-9+/]*={0,2}$/.test(text) ||
text.length % 4 === 1) {
throw new Error("Invalid Base64 text format");
}
const padded = text + "=".repeat((4 - text.length % 4) % 4);
const bytes = Uint8Array.from(atob(padded), ch => ch.charCodeAt(0));
return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
}
function encodeSubscription(linkText) {
const bytes = new TextEncoder().encode(linkText);
let binary = "";
for (const byte of bytes) binary += String.fromCharCode(byte);
return btoa(binary);
}
- If decoding reveals
vmess://orvless://, check the share links one line at a time. - If the raw content starts with
{or[, first check whether it’s JSON. Don’t Base64-decode it yet. - If you see HTML, a sign-in prompt, or an API error, check subscription access, expiration, and the network response.
- After decoding the outer layer, a
vmess://link may still contain a Base64-encoded payload. That’s an encoding layer within the link, not a failure to decode the subscription.
3. Convert share links to JSON by mapping fields
Converting a link isn’t a matter of replacing its prefix. A common VMess share format places an encoded JSON payload after vmess://. VLESS share links typically use a URI structure containing a user ID, host, port, query parameters, and remark. Support for extended parameters varies across clients.
vless://[email protected]:443?encryption=none&security=tls&type=tcp&sni=example.com#format-demo
This fictional connection illustrates the fields; example.com is a documentation-only domain and won’t work for a real connection. Here, encryption=none is a VLESS user-level setting and doesn’t disable TLS; transport security is specified by security=tls.
| Link information | Common JSON field | What to check |
|---|---|---|
| Host and port | settings.vnext[].address、port | The port must be numeric; don’t enter the domain as the remark |
| User ID | settings.vnext[].users[].id | Preserve the full UUID, without URI delimiters |
encryption=none | users[].encryption | This is a VLESS field; it doesn’t apply to VMess |
security=tls | streamSettings.security | Don’t confuse this with VMess user encryption settings |
sni=example.com | tlsSettings.serverName | Preserve the TLS name required by the server |
type=tcp | streamSettings.network | Check the transport name and accepted aliases against the core version |
This example targets Xray cores that support these fields. 10808 binds to the loopback address; 443 is the remote port. They aren’t interchangeable. This example uses the traditional tcp syntax; check the relevant core documentation for transport names and compatibility aliases.
{
"inbounds": [
{
"tag": "local-socks",
"listen": "127.0.0.1",
"port": 10808,
"protocol": "socks",
"settings": {
"auth": "noauth",
"udp": true
}
}
],
"outbounds": [
{
"tag": "proxy",
"protocol": "vless",
"settings": {
"vnext": [
{
"address": "example.com",
"port": 443,
"users": [
{
"id": "7c9c1f0b-4bd8-4b44-8eb2-3eb2ac702783",
"encryption": "none"
}
]
}
]
},
"streamSettings": {
"network": "tcp",
"security": "tls",
"tlsSettings": {
"serverName": "example.com"
}
}
}
]
}
4. Convert JSON to share links: what gets lost
When exporting a share link from a full config, converters usually extract just one proxy outbound. A JSON config can contain multiple outbounds, including direct or blocked outbounds. Before converting, select the target proxy’s tag; don’t assume the first item in the array is the node you want.
Conversion strategy: handle connection and full-config migration separately
Migrate a connection only
- Extract the protocol, server, port, and identity fields from the target outbound.
- Preserve the TLS name, transport path, and any flow control required by the server.
- Generate a share link, then configure the local proxy and routing in the target client.
Preserve the complete behavior
- Keep the native JSON and confirm that the target core can read it.
- Check inbound ports, DNS, routing rules, and external data files.
- Use the client’s custom-config import option and check whether it rewrites the config.
Share links are suited to transferring connection parameters. Preserving the full runtime behavior requires the config file, its dependencies, and compatible core capabilities.
routing.rules, dns, local listening addresses, and per-app settings usually aren’t included in standard node share links. Convert JSON to a link and back, and the resulting config may not match the original—even if the connection works, that doesn’t prove the routing behavior is unchanged.
- Save a copy of the original JSON and note the outbound
tagyou’re migrating. - Compare the server address, port, identity, transport, TLS, and flow control—not just the node name.
- Encode remarks and query parameters according to URI rules. Pay special attention to
?,&, and#in paths so they aren’t mistaken for link delimiters. - If the converter reports unsupported fields, stop the automatic migration and check them manually. Don’t silently drop fields and continue.
5. Repackage share links as a subscription: updates don’t come back automatically
Join multiple share links with one link per line, then Base64-encode the full text to create content for a traditional link-list subscription. This is only a snapshot, though. For a client to fetch it regularly, you’ll also need a subscription URL that you or a trusted provider manages and keeps accessible.
- Prepare the input: Keep one complete link per line, remove blank lines, and don’t encode webpage titles or explanatory text.
- Encode the text: Use UTF-8 consistently and check that remarks containing non-ASCII characters are encoded correctly. Don’t process Unicode text with methods that only support single-byte characters.
- Check the output: Decode the result and confirm that the number and order of links and their parameters are unchanged.
- Plan for updates: A static snapshot won’t change when node addresses or credentials change. Regenerate and republish the content as needed.
Native JSON can’t be turned into a subscription supported by every client by simply Base64-encoding the whole file. Encoding doesn’t change the underlying data structure. If the target import option only accepts a list of share links, a decoded full JSON config may still fail to import.
6. Verify conversion results in v2rayN and v2rayNG
Import into a separate test group first to avoid subscription updates overwriting existing manual configs. The menu names below are common examples; labels and layouts vary by version, so follow the current interface. Don’t paste a full JSON config into the subscription URL field.
- v2rayN subscription: Open “Subscription Group” → “Subscription Group Settings,” add a test group with a trusted subscription URL, then run the update command for that group.
- v2rayN share links: Copy the links, choose “Import bulk URLs from clipboard” in the main window, and check which group contains the new entries.
- v2rayNG share links: Tap “+” in the upper-right corner → “Import from clipboard.” Then edit the new entry and check the server, port, and transport settings.
- v2rayNG subscription: Add the URL under “Subscription Group Settings” in the menu. Return to the main screen, choose “Update subscription,” and confirm that the test group is being updated.
- Native JSON: Use the custom-config import option available in your current version. First check the config parsing results in the log, then start the connection.
After starting, check three layers: first, whether the core parses the config and listens on the local port; second, whether the remote handshake succeeds; and third, whether app traffic enters the proxy and follows the expected route. Seeing a node in the list only confirms that the parser saved the data—it doesn’t replace the other two checks.
Why does import fail even though decoding shows links?
Check whether multiple links were pasted on one line or whether quotes and commas came along with them. Try importing a single link first, then check whether the target core supports its protocol and transport. If one link fails, re-encoding the whole list won’t help.
Import succeeded, but startup reports that the port is in use. What should I do?
Check inbounds[].port in the custom JSON. If another process is using the example port 10808, stop the conflicting process or choose another port, and update the app’s proxy address to match. There’s no need to change the remote 443 port.
TLS handshake fails after conversion. Should I disable certificate verification?
First compare the server address, serverName, system time, and transport settings. Check whether the converter dropped SNI or changed the TLS type. Fix the fields instead of masking a mismatch by disabling certificate verification.
Why does the node connect, but routing between mainland China and elsewhere has changed?
Share links usually don’t include the original routing and dns settings. Select a routing config again in the target client, then check the rule order and data files. To reproduce the original setup, migrate the full JSON and verify core compatibility.
Why did my manual changes disappear after updating the subscription?
Subscription entries may be recreated during updates. Copy any settings you want to keep into a separate manual group, then check the original subscription parameters with the provider. Don’t rely on subscription-generated entries to preserve local edits.
Keep three things: a secure copy of the original input, the field mapping used during conversion, and the verification results in the target client. If something goes wrong later, you can tell whether the subscription response changed, the conversion dropped fields, or local routing or port settings were modified.