サブスクリプション形式の解説:base64・ネイティブ JSON・共有リンクの相互変換

サブスクリプションのURLを開くと文字化け、vmess リンクをインポートするとノード名が「?」に、手書きの JSON は1フィールド足りないだけで接続できない。3形式の境界はどこにあり、変換のどの工程でフィールドが抜けやすいのか、本記事ではフィールドごとに対照します。

この記事の要点

base64 サブスクリプション、ネイティブ JSON 設定、vmess・vless 共有リンクの3形式を分解して比較し、「サブスクリプション → リンク一覧 → 単一ノードのフィールド → ネイティブ JSON」という完全な変換経路を示したうえで、変換中に最も失われやすいフィールドを列挙します。すでにノードに接続できており、設定を手作業で編集したり、クライアント間で移行したりする必要がある読者向けです。

3つの形式とは何か

同じノード情報でも、載せる器が違えば見た目はまったく変わります。base64 サブスクリプションは一括リスト、ネイティブ JSON は完全な設定、共有リンクは1件分のレコードです。

3者の情報量は等価ではありません。サブスクリプション内の1本のリンクは通常1つのアウトバウンド(outbound)しか記述しませんが、ネイティブ JSON はさらにインバウンド、ルーティング、DNS、ログといったクライアント側の設定も担います。ここを混同することが、以降の変換トラブルすべての出発点です。

項目base64 サブスクリプションネイティブ JSON共有リンク
含まれる内容複数行の共有リンクアウトバウンド・インバウンド・ルーティングを含む完全な設定単一ノードの接続パラメータ
自動更新クライアントが定期的に取得ファイルを手動で差し替え非対応
主な入手元パネルからの一括エクスポートサーバー側の設定ファイル、または手書きクライアントやパネルから1件ずつコピー
向いている用途複数端末で1つのノードリストを共有細かい振り分けとローカルポートの固定一時的なインポート、クライアント間の移行
1 回
サブスクリプション全体のデコード回数
2 層
vmess ノードのデコード層数の合計
443
TLS と Reality の常用ポート
0
VMess の alterId のデフォルト値

base64 サブスクリプションのエンコード規則

base64 サブスクリプションはエンコードが1層増えるだけです。サーバー側が複数行の共有リンクをプレーンテキストとして連結し、全体を一度 base64 にして HTTP レスポンスとして返します。クライアントは取得後にまずデコードし、1行1件のリンク一覧を得ます。

デコードに失敗した場合、クライアントは平文として処理する動作にフォールバックするため、同じ解析ロジックで base64 と平文の両方のサブスクリプションを扱えます。自分でデコードするときは、次のコマンドだけ覚えておけば十分です。

# サブスクリプションが返す原文を sub.txt に保存し、全体を一度デコード
base64 -d sub.txt > nodes.txt    # GNU coreutils
base64 -D sub.txt > nodes.txt    # macOS / BSD

wc -l nodes.txt                  # 行数は通常ノード数と一致
head -n 2 nodes.txt

デコード結果の各行の先頭がプロトコルのプレフィックスです。よく見るのは vmess://vless:// です。1つのサブスクリプション内で複数のプレフィックスが混在していても正常で、クライアントは行ごとに解析します。

デコード結果の1行目が vmess:// でも vless:// でもない場合、元データはもともと平文のサブスクリプションなので、これ以上デコードする必要はありません。

共有リンクのフィールド構造

vmess と vless の違いはエンコード方式にあります。vmess リンクは「base64 で JSON を包んだもの」、vless リンクは「URI にクエリパラメータを付けたもの」です。前者をデコードするとフィールド名はすべて略号ですが、後者はアドレスバーでそのまま読めます。

vmess:// の後ろの base64 全体を一度デコードすると、次のオブジェクトが得られます。

{
  "v": "2",              // リンク形式のバージョン、常に 2
  "ps": "香港-01",      // 備考。インポート後はノード名になる
  "add": "example.com", // サーバーアドレス
  "port": "443",        // ポート。ここでは文字列
  "id": "b831381d-6324-4d53-ad4f-8cda48b30811",
  "aid": "0",           // alterId。VMess のみ
  "scy": "auto",        // 暗号化方式
  "net": "ws",          // トランスポート層
  "type": "none",       // 偽装タイプ
  "host": "example.com", // WS の Host ヘッダー
  "path": "/ws",       // WS のパス
  "tls": "tls"          // TLS を有効にするか
}

vless リンクには内側の base64 がなく、すべてのパラメータが ? 以降のクエリ文字列に書かれ、# の後ろが備考です。パラメータ名は vmess の略号より直感的ですが、URI の規則に従って URL エンコードが必要です。

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

2つのリンクが表しているのは同じ内容で、違うのはフィールド名と配置だけです。以下の4枚のカードで、よく使うフィールド、ネイティブ JSON のトップレベル構造、ローカルポートの取り決めを並べて対照します。

vmess リンクのフィールド

add / port
アドレスとポート。port は文字列
id / aid
UUID と alterId
scy
暗号化方式。通常は auto か none
net / type
トランスポート層と偽装タイプ
path / host
WS のパスと Host ヘッダー

全体が base64(JSON)なので、一度デコードすれば読めます。

vless リンクのパラメータ

encryption
常に none。省略不可
security
none / tls / reality
sni
TLS 証明書のドメイン
flow
Reality ノードでは xtls-rprx-vision を指定
pbk / sid
Reality の公開鍵とショート ID

パラメータはクエリ文字列に書く。URL エンコードに注意。

ネイティブ JSON のトップレベル

inbounds
ローカル待ち受け。例:SOCKS 10808
outbounds
アウトバウンドノード。tag と streamSettings を含む
routing
振り分けルール。outboundTag でアウトバウンドを指定
dns
解決方式と上流サーバー
log
ログレベル。調査時は debug

フィールド名は大文字小文字を区別し、port は数値でなければなりません。

ローカルポートの取り決め

SOCKS
10808
HTTP
10809
ログレベル
warning / debug
アウトバウンドの tag
proxy、direct、block の3つがよく使われる名前

ポートと tag はローカル設定で決まるもので、サブスクリプションとは無関係です。

3つの形式を相互変換する方法

変換経路は4ステップで固定です。サブスクリプションからリンク一覧をデコードし、リンクをフィールドに戻し、フィールドをネイティブ JSON に組み立てます。逆にたどればエクスポートになります。

  1. サブスクリプションの原文を取り出す

    ブラウザでサブスクリプションのURLを開き、返ってきた内容を丸ごと sub.txt として保存します。返ってくるのは base64 の塊の場合もあれば、平文のリンク一覧の場合もあります。

  2. リンク一覧をデコードする

    base64 -d sub.txt > nodes.txt を実行すると、1行1件の共有リンクが得られます。プロトコルのプレフィックスは行頭に、備考は行末の # の後にあります。

  3. 単一ノードを復元する

    vmess は vmess:// の後ろをもう一度 base64 デコードして JSON を得ます。vless は ? 以降のクエリパラメータをそのまま読めばよく、追加のデコードは不要です。

  4. ネイティブ JSON に組み立てる

    フィールドを outboundsvnextstreamSettings に埋め込みます。port は数値に変更し、tag は自分で命名し、address にはプロトコルのプレフィックスを付けません。

4ステップ目で組み立てたアウトバウンドはおおよそ次のようになります。フィールドは上記の vless リンクと1対1で対応しています。

{
  "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"
      }
    }
  }] // アウトバウンドの配列
}

逆変換も同じ理屈です。ネイティブ JSON の settings.vnext[0]streamSettings はリンクパラメータを展開した形なので、addressportid を取り出し、プロトコルの規則に従って URI にエンコードし直せば済みます。VMess の場合は alterIdaid に書き戻す必要もあります。

クライアント側にはもっと手軽な経路も2つあります。v2rayN は「カスタム設定」タイプのサーバーに対応しており、完全な JSON を設定欄に貼り付けるとカーネルが直接読み込み、クライアントがアウトバウンドを組み立てる必要がなくなります。逆に、サブスクリプション一覧から単一ノードの共有リンクをコピーし、別の端末の v2rayNG にそのまま貼り付けて「⋮」→「クリップボードから設定をインポート」で取り込むこともできます。

変換時に最も失われやすいフィールド

フィールドの欠落はほぼすべて手作業の受け渡しで起きます。コピー&ペースト時の切れ、文字列と数値の混用、略号の1文字見落としなどです。以下の箇所を順に照合すれば、「インポートは成功したのに接続できない」ケースの大半をカバーできます。

注意

ネイティブ JSON を手で編集するときは、保存前にエディタで JSON の構文チェックを1回かけてください。末尾のカンマと引用符の欠落が最も多い2大エラーです。調査段階ではまず log.logleveldebug に設定し、カーネルにエラーのあるフィールドをログへ出力させ、特定できたら warning に戻します。

どの場面でどれを使うか

3つの形式に優劣はなく、役割分担があるだけです。判断基準は2つだけです。ノード一覧が変わるかどうか、そしてローカルに振り分けルールが必要かどうか。

選定基準:ノードが変わるか、ローカル振り分けが必要か

base64 サブスクリプション
  • 1つのアドレスで全ノードを管理
  • 端末を変えても入力は1回だけ、リストは自動同期
  • ノードの追加・削除はサーバー側が決め、ローカルは変更不要
  • 複数端末でノードが調整される場面向け
ネイティブ JSON
  • routing の振り分けルールと DNS ポリシーを書ける
  • ローカルの SOCKS 10808、HTTP 10809 ポートが固定
  • ノードの変更時はファイルを手動で差し替え
  • 単一端末で細かい制御が必要な場面向け

両者は併用できます。サブスクリプションがノード一覧を担い、ネイティブ JSON の routing がどの通信をプロキシ経由にするかを決めます。

共有リンクはその中間に位置します。サブスクリプションより一時的なインポートに向き、ネイティブ JSON よりクライアント間のコピーに向いています。同じリンクを v2rayN と v2rayNG に貼り付けても、得られるアウトバウンドのパラメータは同じで、違いはローカルポートと振り分けルールをクライアント自身の設定に持たせる点だけです。

実際によく使われる組み合わせは、サーバー側で1つのサブスクリプションURLを維持し、クライアント側でローカルのルーティングルールを追加する形です。ノードはサブスクリプションに追従して更新され、振り分けロジックはローカルに残るので、双方が干渉しません。

よくある質問

サブスクリプションのURLをブラウザで開くと文字化けする?

それは base64 の原文であり、エラーではありません。丸ごとコピーして一度デコードするか、そのままアドレスをクライアントのサブスクリプション設定に入力して更新してください。

vmess リンクをインポートするとノード名が「?」になる?

ps フィールドは UTF-8 の中国語なので、デコードツールが GBK として処理すると文字化けします。UTF-8 でデコードし直してからインポートしてください。

vless リンクをインポートすると flow が不足していると表示される?

Reality ノードでは flowxtls-rprx-vision を指定し、あわせて pbksidfp の3つのパラメータを補ってください。

自分で書いた JSON が無効な設定と判定される?

まず末尾のカンマと引用符を確認し、次に port を文字列から数値に直し、最後に routing.rules[].outboundTag とアウトバウンドの tag が一致しているかを照合してください。

3つの形式の変換関係はそれほど複雑ではありません。サブスクリプションを1層デコードするとリンクが得られ、vmess リンクをもう1層デコードするとフィールドが得られ、フィールドを展開すればネイティブ JSON になります。本当に時間がかかるのはデコードではなく、フィールドを1つも落とさず正しい位置に移す作業です。

v2rayN / v2rayNG をダウンロード

Windows、macOS、Linux のデスクトップ版と Android 版の入口はダウンロードページにあります。サブスクリプションのインポートと振り分け設定はチュートリアルをご覧ください。

クライアントをダウンロード