RSC Payload(RSCペイロード)とは?中身の読み方と取得手順
RSC Payloadは、Next.jsのApp Routerがサーバーからブラウザへ送っている実体そのものです。名前は解説記事でよく見かけますが、「実物はどこにあり、どう開くのか」まで書かれた日本語資料は多くありません。この記事ではNext.js公式ドキュメントの定義とNext.js・React本体のソースコードを一次情報として、RSC Payloadを手元で取り出す手順、行の書式と$Lなどの記号の読み方、ペイロードを膨らませない設計判断とCVE-2025-55182への対応までを扱います。RSC自体の概念整理はReact Server Components(RSC)とは?サーバー・クライアントの違いと使い分けで先に押さえておくと読みやすくなります。
まとめ:RSC Payloadで最初に押さえる4点
- 正体:レンダリング済みServer Componentツリーのコンパクトな表現。HTMLではなく、JSONを拡張したReactの内部プロトコル(Flight)で書かれた行の連なりです。
- 取得方法:初回ロードは
self.__next_fに積まれ、画面遷移ではrsc: 1ヘッダ付きリクエストの応答(Content-Typeはtext/x-component)として届きます。 - 読み方:各行は「16進ID+コロン+1文字タグ+本体」。
IはClient Componentのモジュール参照、$L1は1番の行への遅延参照、$@はPromiseです。 - 注意点:Server ComponentからClient Componentへ渡したpropsは全フィールドがここに載ります。さらにFlightのデシリアライザにCVSS 10.0のRCE(CVE-2025-55182)が出ているため、Next.jsとReactのバージョン確認が必須です。
RSC Payloadの定義とHTML・JSONとの役割分担
公式定義に書かれている3つの中身
Next.js公式ドキュメント「Server and Client Components」(docs version 16.2.12、最終更新2026-06-23)は、RSC Payloadを「レンダリングされたReact Server Componentツリーのコンパクトなバイナリ表現」と定義し、含まれるものを3つ挙げています。Server Componentのレンダリング結果、Client Componentを描画すべき位置のプレースホルダとそのJavaScriptファイルへの参照、そしてServer ComponentからClient Componentへ渡されたpropsです。
ここで見落とされやすいのが3つ目です。propsは「Client Componentが受け取るための入力」であると同時に「ネットワークを流れる公開データ」でもあります。この性質が後述の設計判断とセキュリティの両方に効いてきます。
初回ロードと画面遷移での使われ方の違い
初回アクセスではHTMLとRSC Payloadの両方が届きます。ただし役割は別です。HTMLは非インタラクティブなプレビューを即座に表示するためのもの。RSC Payloadは、Client ComponentツリーとServer Componentツリーを突き合わせる(reconcile)ために使われます。そのあとJavaScriptがClient Componentをハイドレートし、操作可能になります。
App Router内のページ遷移では、HTMLは発行されません。プリフェッチ・キャッシュされるのはRSC Payloadだけで、差分でDOMが更新されます。「同じページなのに初回とリンク遷移でレスポンスの中身が違う」のはこのためです。ハイドレーション時の不一致エラーを追うときも、見るべきは初回ロード側のペイロードになります。詳しい原因切り分けはハイドレーションエラーの原因とReact / Next.jsでの対処法にまとめています。
「payload react」でPayload CMSを探している場合の見分け方
検索クエリ「payload react」「react payload」には、Reactの内部プロトコルを探している人と、ヘッドレスCMSのPayload(Payload CMS)をReactで使う方法を探している人が混在します。判別は単純で、パッケージ名がpayloadや@payloadcms/*ならCMS、react-server-dom-webpackのようなreact-server-dom-*ならこの記事の対象です。CMS側の話題であれば、以降の内容は関係ありません。
ブラウザとcurlでRSC Payloadを取り出す手順
初回ロード:self.__next_f に積まれた断片の結合
初回ロードのペイロードは、HTML内のscriptタグからself.__next_f.push()で少しずつ積まれます。ストリーミングで届いた分から順に積むため、1つの変数に完成品が入っているわけではありません。DevToolsのConsoleで次を実行すると、データチャンクだけを結合して全体が取り出せます。
__next_f.filter(x => x[0] === 1).map(x => x[1]).join('')
__next_fにはデータ以外のpushも混ざるため、先頭要素が1のものに絞っています。ページのHTMLソースを直接検索しても構いません。self.__next_f.push([1,"0:[\"$\",\"main\" のような文字列が並んでいれば、それがペイロードの断片です。
画面遷移:rscヘッダと_rscクエリによる取得
App Router内のリンクをクリックしたときの通信は、Networkパネルでは通常のドキュメント遷移として現れません。Next.jsのソース(packages/next/src/client/components/app-router-headers.ts)が定義しているとおり、リクエストヘッダrsc: 1が付き、レスポンスのContent-Typeはtext/x-componentになります。Networkパネルのフィルタ欄は既定でリクエスト名(URL)を検索するため、x-componentと打っても引っかかりません。MIMEタイプで絞るmime-type:text/x-componentを入力してください。
URLの末尾に付く?_rsc=1a2b3はキャッシュ分離用のハッシュで、同じソースのNEXT_RSC_UNION_QUERYがその実体です。サーバーがペイロードを返すかどうかを決めているのはクエリではなくrscヘッダのほうなので、手動で叩くときはヘッダを忘れないでください。ルーターの状態を厳密に再現したい場合はnext-router-state-tree、プリフェッチ相当ならnext-router-prefetchも併せて送ります。
curlでの生ペイロード取得(rsc: 1 ヘッダ付き)
ブラウザを介さず確認したいときは、rsc: 1を付けてリクエストするだけです。
curl -s https://example.com/dashboard \
-H 'rsc: 1' \
-H 'accept: text/x-component'
返ってくるのは改行区切りの行の連なりです。
Flightフォーマットの行構造とタグの読み方
行の書式は「16進ID+タグ+本体」
Reactのクライアント側パーサ(packages/react-client/src/ReactFlightClient.js)は、ストリームをバイト単位で走査し、コロン(0x3A)までを16進数のIDとして解釈します。続く1バイトがタグで、タグに応じて残りを「改行終端のテキスト」か「バイト数指定のバイナリ」として読みます。
1:I[7767,["931","static/chunks/app/page-8a2c3d.js"],"default"]
0:["$","main",null,{"children":[["$","h1",null,{"children":"Dynamic Page"}],["$","$L1",null,{"initial":1}]]}]
上は書式を分かりやすく整えた例です。IDは登場順ではなく、サーバーが解決できたものから流れます。Iのようなモジュール行はモデル行より先に吐き出されるため、ID 1がID 0より前にあっても異常ではありません。
行タグの一覧と、実務で見かけるもの
タグはReactのソースで文字コードとして分岐しています。本番ビルドで頻繁に見るのは、タグ無しのJSONモデル行とI・H・Tの4つです。Eはサーバー側で例外が起きたときだけ現れます。
| タグ | 意味 | 備考 |
|---|---|---|
| (なし) | JSONモデル | 行が { [ ” 数字で始まる |
| I | モジュール参照 | Client Componentの読み込み情報 |
| H | ヒント | 先読み用のリソース指定 |
| T | テキスト | 長い文字列をバイナリで送る |
| E | エラー | サーバー側で発生した例外 |
| R / r | ReadableStream開始 | rはバイトストリーム |
| X / x | AsyncIterable開始 | xはバイト版 |
| C | ストリーム終了 | 該当IDの完了通知 |
| D / J / W | デバッグ情報 | 開発ビルドのみ |
このほかにN(時刻原点)と、バイト長を先に送るバイナリ行(A・O・o・b・U・S・s・L=ArrayBufferと各TypedArray)があります。DやWが本番環境のペイロードに混ざっていたら、それはビルド構成の事故です。Reactは本番クライアントでW行を受け取ると「Failed to read a RSC payload created by a development version of React on the server while using a production version on the client.」という例外を投げます。サーバーとクライアントでReactのビルドが揃っていない状態を示すので、依存関係の重複を疑ってください。
$付き値の意味
JSONモデル行の中では、文字列の先頭の$が特別な意味を持ちます。["$","main",null,{...}] という4要素配列は「React要素、タグ名、key、props」の並びです。値として現れる$付き文字列は次のように解釈されます。
| 記法 | 意味 |
|---|---|
| $L1f | 行1fへの遅延参照(React.lazy) |
| $@2a | 行2aをPromiseとして参照 |
| $Sreact.suspense | Symbol.for(‘react.suspense’) |
| $h / $T | Server Reference / Temporary Reference |
| $Q / $W / $B / $K | Map / Set / Blob / FormData |
| $D2026-07-27T00:00:00Z | Dateオブジェクト |
| $n9007199254740993 | BigInt |
| $undefined / $NaN / $Infinity | JSONで表現できない値 |
| $$foo | 文字列 $foo のエスケープ |
行の先頭タグと、値の先頭に付く$記法は別の名前空間です。行タグのWは開発用のコンソール出力、値の$WはSetを指します。この表が示すのは、RSC PayloadがJSONの上位互換だという事実です。undefinedやDate、Map、Promiseまで往復できるからこそ、Server Componentのpropsをそのままの型でClient Componentへ渡せます。
module referenceの読み方
I行はClient Componentをブラウザで読み込むための三点セットです。並び順はReactのReactFlightImportMetadata.jsがID = 0/CHUNKS = 1/NAME = 2と定義しており、1:I[7767,["931","static/chunks/app/page-8a2c3d.js"],"default"] なら先頭がモジュールID、次がチャンクIDとファイル名を交互に並べた配列、最後がエクスポート名です。非同期モジュールのときだけ4要素目に1が付き、Reactは要素数が4かどうかで非同期かを判定します。
モデル行側では同じ行IDを$L1として参照するため、「プレースホルダ($L1)」と「実体の読み込み情報(1:I[...])」がペアで届く構造になっています。逆に言えば、I行の数がそのページのClient Component境界の数です。"use client"を貼りすぎた実装ではI行が数十行に膨らむので、境界設計の健康診断として数えてみる価値があります。
ストリーミングとSuspense境界がペイロードに現れる形
$@と後追いチャンクによる段階配信
データ取得が終わっていない部分は、値を待たずに$@(Promise参照)としてIDだけ先に送られます。ブラウザはその時点でフォールバックUIを描画し、サーバー側で解決したタイミングで該当IDの行が後から流れてきて、その場所だけが差し替わります。loading.tsxやSuspenseで囲んだ範囲が段階的に表示されるのは、この後追いチャンクの効果です。
途中で切れたペイロードの見分け方
ストリームが最後まで届かないと、$@や$Lで参照されているIDの行が現れないまま接続が終わります。画面上は「ローディング表示のまま固まる」形になり、JavaScriptのエラーも出ないことが多いため原因を追いにくい不具合です。判定材料はC行(文字コード67、該当IDのストリーム終了通知)で、参照されているIDに対するC行が無いまま応答が終わっていれば途中切断です。curlで生のペイロードを取得して確認するのが最短の切り分けになります。プロキシやCDNがレスポンスをバッファリングしている場合も同じ症状になります。
RSC Payloadを肥大化させない設計判断
Client Componentへ渡すpropsの絞り込み基準
ここは明確に言い切ります。ORMのエンティティやAPIレスポンスをそのままClient Componentのpropsに渡す実装は避けてください。 公式定義のとおり、渡したpropsは全フィールドがRSC Payloadに載ります。画面に表示していないメールアドレスや内部フラグも、ページのソースを開けば誰でも読める状態になります。表示に必要なフィールドだけを抜き出して渡すのが原則です。
データ量の面でも効きます。一覧ページで100件のレコードを丸ごと渡せば、そのぶんペイロードが太り、画面遷移のたびに転送されます。
childrenスロットによるServer Componentの差し込み
「Client Componentの内側にサーバーで取得した内容を置きたい」場合、データをpropsで渡すのではなく、Server Componentをchildrenとして差し込みます。Next.js公式が示すModalとCartの例がこの形です。この場合Server Componentはサーバーでレンダリング済みの結果としてペイロードに入るため、元データそのものは送られません。
RSC Payloadを自前APIとして扱わない理由
ペイロードをキャッシュサーバーに保存して配信する、あるいは外部システムに渡して解釈させる——こうした使い方は推奨しません。Flightはバージョン間の互換性を公開仕様として保証していない内部プロトコルです。前掲の「開発版Reactが作ったペイロードを本番版クライアントが読めず例外になる」挙動は、同じReactの同じバージョン系列であっても読み手と書き手が揃わなければ壊れることを示しています。加えて、外部から受け取ったペイロードをデシリアライズする経路は、次に述べる攻撃面そのものです。
Flightデシリアライザの脆弱性CVE-2025-55182と対応版
影響を受ける3パッケージと修正版19.0.1/19.1.2/19.2.1
2025年12月3日に公開されたCVE-2025-55182(GHSA-fv66-9v8q-g76r、通称React2Shell)は、Flightプロトコルのデシリアライズ処理に起因するリモートコード実行の脆弱性で、CVSS基本値は最大値の10.0です。細工されたペイロードを受け取った際に構造の検証が不十分で、サーバー上で任意のコードを実行される可能性がありました。影響はreact-server-dom-webpack/react-server-dom-turbopack/react-server-dom-parcelに及びます。
| 影響を受ける版 | 修正版 |
|---|---|
| 19.0.0 | 19.0.1 |
| 19.1.0 以上 19.1.2 未満 | 19.1.2 |
| 19.2.0 | 19.2.1 |
npm registryで確認すると、2026年7月27日時点のreact-server-dom-*のlatestは3パッケージとも19.2.8(公開2026-07-21)です。ただしNext.jsアプリでnpm ls react-server-dom-webpackを叩いても何も出ません。Next.jsはnext/dist/compiled配下にこれらを同梱しており、nextのdependenciesには現れないためです。
Next.js側のCVE-2025-66478と修正版7つ
Next.jsは同じ問題を下流のCVE-2025-66478(GHSA-9qr9-h5gf-34mp)として追跡しています。影響を受けるのはApp Routerを使う15.x・16.x、および14.3.0-canary.77以降のcanaryです。13.x・14.x stable・Pages Router・Edge Runtimeは影響を受けません。修正版は15.0.5/15.1.9/15.2.6/15.3.6/15.4.8/15.5.7/16.0.7で、canaryは15.6.0-canary.58と16.1.0-canary.12です。npx fix-react2shell-nextが現在の版を判定して該当版へ引き上げます。公式は、未パッチのまま公開していたアプリケーションについてシークレットの再発行も求めています。使用中のNext.jsの版を把握しておきたい場合はNext.js 16の概要と主要機能を、脆弱性そのものの解説はReact Server Componentsの脆弱性(CVE-2025-55182)とは何かを参照してください。
Server Actionsも同じ経路という前提での防御方針
この脆弱性が教えているのは、RSC Payloadは「サーバーが送るもの」であると同時に「サーバーが受け取るもの」でもあるという点です。Server Actionsの引数も同じFlightの仕組みでシリアライズされ、サーバー側でデシリアライズされます。つまりデシリアライザは常に外部入力にさらされています。Pages Routerが非影響だったのは、そもそもこの経路を持たないからです。App Routerを使う以上この層は避けられないため、フレームワークとReactのバージョンを揃えて追随することが最も確実な防御になります。
よくある質問
RSCは何の略で、RSC Payloadはどう読みますか?
RSCはReact Server Componentsの略です。RSC Payloadは「アールエスシー ペイロード」と読み、日本語表記では「RSCペイロード」と書かれます。「RSC」という略語はReact以外の分野でも使われるため、検索時は「rsc react」のようにReact関連の語を添えてください。
「payload react」で出てくるPayload CMSとRSC Payloadは同じものですか?
まったく別物です。Payload CMSはTypeScript製のヘッドレスCMSで、npmパッケージ名はpayloadや@payloadcms/*です。RSC PayloadはReact本体の内部データ形式で、実装はreact-server-dom-webpackなどにあります。検索結果でどちらの話題かを判別するには、記事中に出てくるパッケージ名を見るのが最も確実です。
RSC Payloadを実装しているパッケージはどれですか?
バンドラごとに分かれており、react-server-dom-webpack、react-server-dom-turbopack、react-server-dom-parcel、および@vitejs/plugin-rscがあります。エンコード側とデコード側の共通ロジックはReact本体のreact-client/react-serverパッケージにあり、行のパース処理はReactFlightClient.jsに実装されています。アプリケーション開発者がこれらを直接importすることは通常ありません。
RSC PayloadとData Cacheはどう違いますか?
キャッシュの層が違います。Data Cacheが保持するのはfetchなどで取得した元データで、RSC Payloadが保持するのはそのデータを使ってレンダリングした結果です。Next.jsは静的ルートについてビルド時や初回リクエスト時にRSC Payloadを生成して保存し、以降のリクエストではレンダリングを省略して保存済みのペイロードを返します。なおNext.js 16でcacheComponentsを有効にすると、この区別はuse cacheのdata-levelキャッシュとUI-levelキャッシュとして再整理されます。Data Cache/Full Route Cacheという名称はcacheComponentsを使わない従来モデル側の用語です。
RSC PayloadはApp Routerでしか使えませんか?
Next.jsに限れば、RSC Payloadを使うのはApp Routerだけです。Pages RouterはgetServerSidePropsなどで取得したデータをJSONとしてHTMLに埋め込む方式で、Server Componentsの仕組みを使いません。前述のCVE-2025-66478でPages Routerが対象外だったのはこのためです。ただしRSC Payload自体はNext.js固有ではなく、React Router、Waku、Parcel、Viteの各RSC実装でも同じFlightプロトコルが使われます。
関連記事
- React Server Components(RSC)とは?サーバー・クライアントの違いと使い分けを実装例で解説
- React Server Componentsの脆弱性(CVE-2025-55182)とは何か?基本概要とポイント
- ハイドレーションエラー(Hydration Error)とは?原因とReact / Next.jsでの対処法を解説
- Next.js 16とは?ReactベースのWeb開発フレームワーク最新版の概要や主要機能を詳しく紹介
- React 19.2の新機能総まとめ:<Activity>コンポーネントやuseEffectEvent、cacheSignalなど注目ポイント徹底解説