Subscription Formats Explained: base64, Native JSON and Share Link Conversion

A subscription URL opens as a wall of gibberish, a vmess link imports with a question mark where the node name should be, and a hand-written JSON config refuses to connect because one field is missing. This article maps where the three formats begin and end, and which step of a conversion drops fields, comparing them field by field.

At a glance

It breaks base64 subscriptions, native JSON configs and vmess/vless share links down side by side, lays out the full conversion path from subscription to link list to single-node fields to native JSON, and lists the fields most often lost along the way. Written for readers who can already connect to a node and now need to edit configs by hand or move between clients.

What each of the three formats is

The same node details look completely different depending on the container they travel in. A base64 subscription is a bulk list, native JSON is a full config, and a share link is a single record.

The three do not carry equal amounts of information: one link inside a subscription usually describes a single outbound, while native JSON also has to cover client-side settings such as inbounds, routing, DNS and logging. Mixing this up is where every conversion problem starts.

Aspectbase64 subscriptionNative JSONShare link
What it carriesMultiple share links, one per lineFull config with outbounds, inbounds and routingConnection parameters for one node
Auto updateClient fetches on a scheduleReplace the file by handNot supported
Typical sourceBulk export from a panelServer config file or written by handCopied one at a time from a client or panel
Best forSharing one node list across devicesFine-grained routing and fixed local portsQuick imports and moving between clients
1 time
Decodes for the whole subscription
2 layers
Total decode layers for a vmess node
443
Common ports for TLS and Reality
0
Default alterId for VMess

Encoding rules for base64 subscriptions

A base64 subscription adds exactly one layer of encoding: the server joins multiple share links into plain text, base64-encodes the whole block once, and returns it as an HTTP response. The client fetches it, decodes it first, and gets back a list with one link per line.

When decoding fails, clients fall back to treating the response as plain text, so the same parsing logic handles both base64 and plain-text subscriptions. If you want to decode it yourself, one command is all you need.

# save the raw subscription response as sub.txt, then decode the whole file once
base64 -d sub.txt > nodes.txt    # GNU coreutils
base64 -D sub.txt > nodes.txt    # macOS / BSD

wc -l nodes.txt                  # the line count usually equals the number of nodes
head -n 2 nodes.txt

In the decoded output, each line starts with a protocol prefix, most often vmess:// or vless://. It is normal for one subscription to mix several prefixes; clients parse it line by line.

If the first decoded line is neither vmess:// nor vless://, the source was already a plain-text subscription and needs no further decoding.

Field structure of a share link

The difference between vmess and vless comes down to encoding: a vmess link is base64 wrapping a JSON object, while a vless link is a URI with query parameters. The first decodes into abbreviated field names; the second can be read straight from the address bar.

Decode the entire base64 block after vmess:// once and you get this object:

{
  "v": "2",              // link format version, always 2
  "ps": "Hong Kong-01",      // remark, used as the node name after import
  "add": "example.com", // server address
  "port": "443",        // port, a string here
  "id": "b831381d-6324-4d53-ad4f-8cda48b30811",
  "aid": "0",           // alterId, VMess only
  "scy": "auto",        // encryption method
  "net": "ws",          // transport
  "type": "none",       // header type
  "host": "example.com", // Host header for WS
  "path": "/ws",       // WS path
  "tls": "tls"          // whether TLS is enabled
}

A vless link has no inner base64 layer: every parameter lives in the query string after ?, and the remark sits after #. The parameter names are more readable than the vmess abbreviations, but they still have to be URL-encoded per URI rules.

vless://[email protected]:443?encryption=none&security=reality&sni=www.example.com&fp=chrome&pbk=UuMBgl8KtNqHqY7p&sid=0123abcd&flow=xtls-rprx-vision&type=tcp#Hong Kong-01

Both link types describe the same thing; only the field names and their placement differ. The four cards below line up the common fields, the top-level structure of native JSON, and the local port conventions.

vmess link fields

add / port
Address and port; port is a string
id / aid
UUID and alterId
scy
Encryption method, usually auto or none
net / type
Transport and header type
path / host
WS path and Host header

The whole string is base64 (JSON); one decode makes it readable.

vless link parameters

encryption
Always none; cannot be omitted
security
none / tls / reality
sni
TLS certificate domain name
flow
Use xtls-rprx-vision for Reality nodes
pbk / sid
Reality public key and short ID

Parameters live in the query string; watch the URL encoding.

Native JSON top level

inbounds
Local listeners, e.g. SOCKS 10808
outbounds
Outbound nodes, with tag and streamSettings
routing
Routing rules that point at outbounds via outboundTag
dns
Resolution mode and upstream servers
log
Log level; use debug when troubleshooting

Field names are case-sensitive, and port must be a number.

Local port conventions

SOCKS
10808
HTTP
10809
Log level
warning / debug
Outbound tag
The three common names: proxy, direct, block

Ports and tags are decided by the local config, not by the subscription.

How to convert between the three formats

The conversion path always has four steps: decode the subscription into a link list, restore each link into fields, then assemble those fields into native JSON. Walk it backwards and you have an export.

  1. Fetch the raw subscription

    Open the subscription URL in a browser and save the whole response as sub.txt. What comes back may be one base64 block or a plain-text list of links.

  2. Decode the link list

    Run base64 -d sub.txt > nodes.txt to get one share link per line; the protocol prefix sits at the start of the line and the remark after the trailing #.

  3. Restore a single node

    For vmess, base64-decode the content after vmess:// once more to get JSON; for vless, read the query parameters after ? directly — no further decoding needed.

  4. Assemble native JSON

    Fill the fields into vnext and streamSettings under outbounds: turn port into a number, pick your own tag, and keep address free of any protocol prefix.

The outbound you assemble in step four looks roughly like this, with fields matching the vless link above one for one:

{
  "outbounds": [{
    "tag": "proxy",
    "protocol": "vless",
    "settings": {
      "vnext": [{
        "address": "example.com",
        "port": 443,
        "users": [{
          "id": "b831381d-6324-4d53-ad4f-8cda48b30811",
          "encryption": "none",
          "flow": "xtls-rprx-vision"
        }]
      }]
    },
    "streamSettings": {
      "network": "tcp",
      "security": "reality",
      "realitySettings": {
        "serverName": "www.example.com",
        "publicKey": "UuMBgl8KtNqHqY7p",
        "shortId": "0123abcd",
        "fingerprint": "chrome"
      }
    }
  }] // outbound array
}

The reverse works the same way: settings.vnext[0] and streamSettings in native JSON are just the link parameters spelled out. Pull out address, port and id, then encode them back into a URI following the protocol rules. For VMess, write alterId back into aid as well.

Clients offer two shortcuts. v2rayN supports a "Custom configuration" server type: paste the full JSON into the config box and the core reads it directly instead of the client assembling an outbound. In the other direction, copy a single node's share link from the subscription list and paste it into v2rayNG on another device via "⋮" → "Import config from clipboard".

Fields most often lost in conversion

Dropped fields almost always happen during manual copying: a paste that gets truncated, a string where a number belongs, an abbreviation missing one letter. Check the spots below one by one and you will cover most cases of "import succeeded but nothing connects".

Note

When editing native JSON by hand, run the editor's JSON syntax check before saving; trailing commas and missing quotes are the two most common errors. While troubleshooting, set log.loglevel to debug so the core writes the offending field to the log, then switch it back to warning once you have found it.

Which format fits which scenario

None of the three formats is better than the others; they simply divide the work. Two questions decide it: will the node list change, and do you need routing rules on this machine?

How to choose: whether nodes change, and whether local routing is needed

base64 subscription
  • One URL manages every node
  • Enter it once per device and the list stays in sync
  • Nodes are added or removed server-side, with no local edits
  • Best when you use several devices and nodes change
Native JSON
  • Lets you write routing rules and DNS policies
  • Fixed local ports: SOCKS 10808, HTTP 10809
  • Node changes mean replacing the file by hand
  • Best on a single machine that needs fine-grained control

The two can coexist: the subscription handles the node list, while routing in native JSON decides which traffic goes through the proxy.

Share links sit in the middle: better than a subscription for one-off imports, better than native JSON for copying between clients. Paste the same link into v2rayN and v2rayNG and you get identical outbound parameters; the only difference is that each client keeps local ports and routing rules in its own settings.

In practice the more common setup is a subscription URL maintained on the server plus a local routing ruleset in the client. Nodes follow the subscription updates, routing logic stays local, and neither side interferes with the other.

FAQ

The subscription URL opens as gibberish in a browser?

That is the raw base64, not an error. Copy the whole block and decode it once, or just paste the URL into the client's subscription settings and update.

Node name turns into question marks after importing a vmess link?

The ps field is UTF-8 Chinese text, so a decoder that treats it as GBK produces mojibake. Decode it again as UTF-8 before importing.

Importing a vless link reports a missing flow?

Reality nodes need xtls-rprx-vision in flow, along with all three of pbk, sid and fp.

A hand-written JSON config is rejected as invalid?

Check for trailing commas and stray quotes first, then change port from a string to a number, and finally verify that routing.rules[].outboundTag matches the outbound tag.

The relationship between the three formats is not complicated: decode a subscription once to get links, decode a vmess link once more to get fields, and those fields spelled out become native JSON. The slow part is never the decoding — it is moving every field to the right place without losing one.

Download v2rayN / v2rayNG

Download links for the Windows, macOS and Linux desktop builds and the Android build are on the download page; subscription import and routing setup are covered in the tutorials.

Download the client