V2Rayのサブスクリプション形式を解説:Base64リスト・JSON設定・vmess://リンクの変換

Base64形式のサブスクリプション、共有リンク、JSON設定の違いを解説。v2rayN・v2rayNGの対応形式や、手動デコード・形式変換で間違えやすい項目も紹介します。

この記事の要点

サブスクリプションURLは設定を取得するための入口、Base64はテキストのエンコード方式、vmess://はノードの共有形式、JSON設定はコアの動作を定義するものです。複数の端末でノードを共有したい方、サブスクリプションの内容を確認したい方、カスタム設定を移行したい方に向けて、応答形式の見分け方からローカルでのデコード、プロトコル・トランスポート・TLSの確認方法まで解説します。「解析できた」だけで設定が完全だと判断しないことが大切です。

サブスクリプションURL・ノードリスト・JSON設定の違い

「V2Rayのサブスクリプション」は、単一のファイル形式を指す言葉ではありません。HTTPSのURLから返されるのは、Base64でエンコードされた共有リンクのリスト、プレーンテキストのリスト、完全なJSON、あるいはログインページの場合もあります。拡張子やURLに「subscribe」が含まれるかどうかでは内容を判断できません。クライアントが処理するのは、サーバーから返されたレスポンス本文です。

一般的なBase64形式のサブスクリプションは、2層構造になっています。まずリスト全体をエンコードし、デコードすると各行に共有リンクが並びます。その中に従来形式のJSONを使ったvmess://がある場合、プロトコル接頭辞の後に続くペイロードもBase64でエンコードされています。2つのエンコードは対象が異なるため、サブスクリプション全体を単一のVMessノードとして解析することはできません。

共有リンクリスト形式のサブスクリプション

おすすめ

通常のノード管理に使う複数の共有リンクをまとめた形式です。提供元がアドレスやポートを変更した場合も、クライアントでサブスクリプションを更新すれば新しいリストを取得できます。外側のリストをBase64でエンコードするかどうかは、提供元の出力形式によります。

おすすめの用途:v2rayNとv2rayNGで同じ提供元のノードを管理する

単一のvmess://共有リンク

1つのVMessノードの接続パラメーターを記述した形式で、継続的に更新されるサブスクリプションURLとは異なります。通常はクリップボードやQRコードからインポートします。

おすすめの用途:ノード1つを一時的に移行する、インポート時の問題を項目ごとに調べる

コアネイティブのJSON設定

インバウンド、複数のアウトバウンド、DNS、ルーティングなどを記述できます。単なるノード一覧ではなく、実際に使用するコアの設定仕様に適合している必要があります。

おすすめの用途:インバウンドやルーティングを細かく制御するカスタム設定

v2rayN・v2rayNGで使うインポート方法

まず内容に合ったインポート方法を選び、その後で変換を検討しましょう。HTTPSのサブスクリプションURLをクリップボードからノードとして読み込んだり、完全なJSONをサブスクリプションURL欄に入力したりしても、通常は期待どおりに処理されません。以下では一般的な日本語UIの名称を使っています。バージョンによってメニュー名が異なる場合があるため、項目名だけでなく機能を確認してください。

v2rayNとv2rayNGで同じ共有リンク形式のサブスクリプションを使う

デスクトップ版:v2rayN
  • 「サブスクリプショングループ」→「サブスクリプショングループの設定」を開き、グループを追加してサブスクリプションURLを入力します。
  • 保存後、「サブスクリプショングループ」メニューから「すべてのサブスクリプションを更新(プロキシを使用しない)」を実行します。アクセスにプロキシが必要な場合は、プロキシ経由で更新する項目を選択してください。
  • 共有リンクが1つだけの場合は、「サーバー」→「クリップボードから共有リンクをインポート」を使います。URLがvmess://のサブスクリプショングループは作成しないでください。
Android版:v2rayNG
  • サイドバーから「サブスクリプショングループの設定」を開き、追加ボタンをタップします。備考とサブスクリプションURLを入力して保存してください。
  • メイン画面に戻り、右上のメニューから「サブスクリプションを更新」を実行します。リストに対象グループのノードが表示されるか確認してください。
  • 共有リンクが1つだけの場合は、メイン画面の「+」→「クリップボードからインポート」を使います。完全な設定は、カスタム設定のファイルまたはクリップボードからインポートしてください。

同じ提供元でも、両方のクライアントがまったく同じパラメーターに対応しているとは限りません。更新後はノード数、トランスポートの種類、TLS設定を確認してください。

どちらのクライアントも、一般的な従来形式のJSON VMess共有リンクと、共有リンクリスト形式のサブスクリプションを処理できます。コアネイティブの完全なJSON設定は、カスタム設定のインポート方法を使ってください。「VMessに対応」とあっても、あらゆるJSON形式のサブスクリプションを使えるとは限りません。コア固有の項目を含む設定では、クライアントのバージョンと使用するコアの両方を確認する必要があります。

v2rayNGはXrayコア、v2flyNGはv2flyコアを使用します。画面だけが異なる、同一の設定解釈エンジンではありません。Xray専用の設定をv2flyNGで使う場合は、ファイル名を変更するだけでなく、プロトコルと各項目への対応状況を確認してください。v2rayNでは、その設定が実際にどのコアで実行されるかを確認する必要があります。

ローカルでデコード:まずリストを確認し、次にvmess://のペイロードを確認

以下のJavaScriptは、ローカルのブラウザーの開発者ツールでテキストを確認するためのものです。ネットワークリクエストは発生しません。コードを読んでから、ネットワークに接続していない空のページのコンソールで実行してください。関数が受け取るのは、すでにローカルにコピーした本文です。サブスクリプションのダウンロードは行いません。見知らぬWebサイトのコンソールで認証情報を処理しないでください。

function decodeBase64Utf8(input) {
  let text = input.trim().replace(/\s+/g, "");
  text = text.replace(/-/g, "+").replace(/_/g, "/");
  if (text.length % 4 === 1) {
    throw new Error("Base64の長さが不正です");
  }
  text = text.padEnd(Math.ceil(text.length / 4) * 4, "=");
  const bytes = Uint8Array.from(atob(text), ch => ch.charCodeAt(0));
  return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
}

function encodeBase64Utf8(text) {
  const bytes = new TextEncoder().encode(text);
  let binary = "";
  for (const byte of bytes) binary += String.fromCharCode(byte);
  return btoa(binary);
}

function decodeVmessJson(uri) {
  const text = uri.trim();
  if (!text.startsWith("vmess://")) {
    throw new Error("VMess共有リンクではありません");
  }
  return JSON.parse(decodeBase64Utf8(text.slice(8)));
}

// ローカルのサンプルだけで自己テストし、サブスクリプションの認証情報は使用しません。
console.log(decodeBase64Utf8(encodeBase64Utf8("ノードのサンプル")));
  1. レスポンスを確認:本文にvmess://などのプロトコル接頭辞が行ごとに並んでいる場合は、そのまま行単位で処理し、全体をデコードしないでください。先頭がHTMLタグやログイン案内の場合は、まずサブスクリプションのリクエストに関する問題を解決します。
  2. 外側をデコード:Base64形式だと確認できたリスト本文にdecodeBase64Utf8を適用し、split(/\r?\n/)で行に分割して空行を除外します。
  3. 共有リンクをデコード:従来形式のJSON VMessリンクをdecodeVmessJsonに渡します。add、port、idなどの項目を含むオブジェクトが返る想定です。
  4. むやみに再試行しない:デコード結果がJSONでない場合は、共有形式の違いを確認してください。同じプロトコル接頭辞を使う過去の実装が、すべてこの記事で説明するJSONペイロードの仕様に従っているとは限りません。

atobが返すのはバイト列に対応する文字列であり、UTF-8のデコードまで行うわけではありません。TextDecoderを省略すると、ノード名などの日本語が文字化けすることがあります。日本語を直接btoaに渡すと、エラーになる場合もあります。また、Base64URLの-と_は、標準Base64の+と/とは区別が必要です。サブスクリプションURL自体にこれらの置換を適用しないでください。

4文字
Base64は4文字ごとに最大3バイトを表す
443
以下の例で使うサーバー側TLSポート
10808
以下の例で使うローカルSOCKSインバウンドポート

vmess://とJSON設定の項目の対応関係

以下は説明用の従来形式VMess共有ペイロードです。ドメイン名とUUIDは構造の説明にのみ使用しており、接続可能なサービスを示すものではありません。vは共有形式のバージョンで、コアのバージョンではありません。aidはalterIdを表します。現行のVMess AEAD設定では通常0ですが、移行時はサーバー側の指定に従ってください。

{
  "v": "2",
  "ps": "サンプル VMess WS TLS",
  "add": "node.example.com",
  "port": "443",
  "id": "8a7e4f30-5b91-4f8e-9e42-2c6d1b0a73f5",
  "aid": "0",
  "scy": "auto",
  "net": "ws",
  "type": "none",
  "host": "node.example.com",
  "path": "/gateway",
  "tls": "tls",
  "sni": "node.example.com"
}
共有リンクの項目一般的なコア設定の場所変換時の注意点
add / portsettings.vnext[].address / port共有リンクのポートは文字列、コア設定では数値の場合があります。
id / aid / scyusers[].id / alterId / securityそれぞれUUID、alterId、VMessの暗号化方式を表します。
netstreamSettings.networkこの例はwsです。名前を変更するだけでは、別のトランスポートに変換できません。
host / pathwsSettings.headers.Host / pathWebSocketのリクエストパラメーターであり、サーバーアドレスと同じとは限りません。
tls / snistreamSettings.security / tlsSettings.serverNameTLSとVMessの通信内容の暗号化は、別々の設定です。
psクライアントの備考、またはカスタムアウトバウンドのタグ備考は接続パラメーターではなく、ルーティングルールも自動生成されません。

以下の完全なサンプルでは、一般的なvnext構造を使い、ローカルSOCKSインバウンド1つとVMessアウトバウンド1つだけを示しています。クライアントにインポートしたときにインバウンド設定が維持されるかどうかは、カスタム設定の実行方式によって異なります。実行前に、使用中のコアがこの項目構造に対応しているかも確認してください。

{
  "log": {
    "loglevel": "warning"
  },
  "inbounds": [
    {
      "tag": "local-socks",
      "listen": "127.0.0.1",
      "port": 10808,
      "protocol": "socks",
      "settings": {
        "auth": "noauth",
        "udp": true
      }
    }
  ],
  "outbounds": [
    {
      "tag": "proxy",
      "protocol": "vmess",
      "settings": {
        "vnext": [
          {
            "address": "node.example.com",
            "port": 443,
            "users": [
              {
                "id": "8a7e4f30-5b91-4f8e-9e42-2c6d1b0a73f5",
                "alterId": 0,
                "security": "auto"
              }
            ]
          }
        ]
      },
      "streamSettings": {
        "network": "ws",
        "security": "tls",
        "tlsSettings": {
          "serverName": "node.example.com",
          "allowInsecure": false
        },
        "wsSettings": {
          "path": "/gateway",
          "headers": {
            "Host": "node.example.com"
          }
        }
      }
    }
  ]
}

再エンコードと逆変換:失われる情報に注意

従来形式のJSON VMessペイロードから共有リンクを作るには、JSONをUTF-8でエンコードし、プロトコル接頭辞を付けます。複数のリンクを改行で連結して全体をエンコードすると、一般的なBase64形式のリスト本文になります。以下のコードは前述のエンコード関数に続けて使用します。入力は説明用のサンプルです。

const sample = {
  v: "2",
  ps: "サンプル VMess WS TLS",
  add: "node.example.com",
  port: "443",
  id: "8a7e4f30-5b91-4f8e-9e42-2c6d1b0a73f5",
  aid: "0",
  scy: "auto",
  net: "ws",
  type: "none",
  host: "node.example.com",
  path: "/gateway",
  tls: "tls",
  sni: "node.example.com"
};

const share = "vmess://" + encodeBase64Utf8(JSON.stringify(sample));
const links = [share];
const subscriptionBody = encodeBase64Utf8(links.join("\n"));

console.log(share);
console.log(subscriptionBody);

リスト本文を生成しても、サブスクリプションサービスができるわけではありません。クライアントからアクセスできるURLに内容を置き、適切なアクセス制御を設定して初めて、更新可能なリモートの入口になります。端末間の移行だけであれば、クライアントから共有リンクをエクスポートして使えばよく、別途ファイルを公開する必要はありません。

インポート失敗・ノード数が0・日本語の文字化けを解決する

サブスクリプションの更新は完了したのに、ノード数が0なのはなぜ?

まず、正しいグループを表示しているか確認し、更新ログに解析結果が記録されているか確認してください。レスポンス本文をローカルで確認します。ログインページ、トークンの有効期限切れ、非対応の設定構造が返されている場合、Base64を何度デコードしても解決しません。提供元からv2rayN・v2rayNG向けの共有リンクリスト形式を入手してください。

デコードでエラーになります。コピー漏れでしょうか?

チャットアプリによって省略記号が挿入されていないか、引用符が混入していないか、長さを4で割った余りが1になっていないか確認してください。末尾のパディングが足りない場合は補正できますが、本文が途中で切れている場合は、イコールを追加しても復元できません。HTTPSのサブスクリプションURL全体にデコード関数を使わないでください。

ノード名が文字化けしています。接続パラメーターは使えますか?

まずUTF-8でデコードし直し、ペイロード内のpsを確認してください。文字化けが備考だけなら、通常は接続に直接影響しません。パスなど他の項目にASCII以外の文字が含まれる場合は、1つずつ確認が必要です。元のテキストを保管し、文字化けした結果を繰り返し再エンコードしないでください。

インポートには成功したのに接続がタイムアウトします。形式を変換し直す必要がありますか?

まずサーバーアドレスを確認し、ポート443が提供元から指定された値であることを確かめます。次にUUID、WebSocketパス、Host、SNIを確認してください。ログにポート10808の使用中と表示される場合は、ローカルのインバウンド設定の競合を解消します。TLS名のエラーであれば、サーバー名を修正してください。同じ内容を何度もエンコードし直しても解決しません。

手動で変更したノードが、サブスクリプションの更新後に元に戻るのはなぜ?

サブスクリプションのノードは通常、リモートの内容から再生成されます。ローカルでの変更を残したい場合は、先に独立した設定としてエクスポートまたはコピーし、その設定を編集してください。変更の保持方法はクライアントのバージョンによって異なります。サブスクリプションURLは提供元のデータを更新するもので、ローカルの編集内容を提供元へ自動で同期するものではありません。

確実に移行するには、元のサブスクリプション本文、デコード後のノード項目、インポート後のクライアント設定の3つを保存して比較できるようにします。検証ではまず1つのノードで接続を確認し、その後リスト全体に進みます。JSON設定のDNSやルーティングを移行する場合は、高度な設定を参考に項目を確認してください。項目の意味がわからない場合は、複数の変換ツールを試す前に用語集を確認すると、違いを見つけやすくなります。

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