GitHub Actionsのキャッシュを読み取り専用にするには、.github/workflows 配下のワークフローYAMLに cache-mode: read を追加します。ワークフロー全体に適用できるほか、jobs.<job_id>.cache-modeでジョブ単位に上書きできます。ジョブ側の設定が優先されるため、全体は読み取り専用にし、信頼できるキャッシュ更新ジョブだけ保存を許可する構成が基本です。
cache-modeは2026年9月10日にGitHub.comの全プランで一般提供されました。キャッシュの復元と保存を別々に制御でき、キャッシュポイズニング対策として最小権限を適用できます。([GitHub Blog][1])
最初に結論:cache-modeをreadに設定する
ワークフロー内のすべてのジョブでキャッシュ保存を禁止したい場合は、cache-mode: readをトップレベルに記述します。
name: CI
on:
push:
pull_request:
cache-mode: read
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Restore npm cache
uses: actions/cache@v4
with:
path: ~/.npm
key: npm-${{ runner.os }}-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
npm-${{ runner.os }}-
- run: npm ci
- run: npm test
この設定では、testジョブは既存キャッシュを復元できますが、新しいキャッシュは保存できません。
トップレベルのcache-modeは、ワークフロー内のすべてのジョブに適用されます。ただし、個別のジョブにjobs.<job_id>.cache-modeが設定されている場合は、ジョブ側の値が優先されます。([GitHub Docs][2])
cache-modeで指定できる4つの設定値
cache-modeでは、キャッシュの復元と保存を次の4段階で制御できます。
| 設定値 | キャッシュの復元 | キャッシュの保存 | 主な用途 |
|---|---|---|---|
read | 可能 | 不可 | テスト、Lint、プルリクエストの検証 |
write | 可能 | 可能 | 信頼できるブランチのビルド |
write-only | 不可 | 可能 | クリーンな環境からキャッシュを生成する専用ジョブ |
none | 不可 | 不可 | デプロイ、署名、キャッシュ不要のジョブ |
([GitHub Docs][2])
特に間違えやすいのが、writeとwrite-onlyの違いです。
writeは、既存キャッシュを復元したうえで、新しいキャッシュも保存できます。一方、write-onlyは保存専用であり、既存キャッシュを復元しません。
キャッシュを利用しながら更新したい通常のビルドにはwrite、古いキャッシュを一切取り込まずに新しいキャッシュを作りたいジョブにはwrite-onlyが適しています。
ジョブごとにキャッシュ保存権限を変更する
実務では、ワークフロー全体をreadにして、キャッシュ更新が必要なジョブだけwriteまたはwrite-onlyにする構成が安全です。
次の例では、テストジョブは読み取り専用とし、mainブランチへのpushで実行されるキャッシュ生成ジョブだけ保存を許可しています。
name: CI
on:
push:
branches:
- main
pull_request:
cache-mode: read
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Restore npm cache
uses: actions/cache@v4
with:
path: ~/.npm
key: npm-${{ runner.os }}-${{ hashFiles('**/package-lock.json') }}
- run: npm ci
- run: npm test
cache-warm:
needs: test
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
cache-mode: write-only
steps:
- uses: actions/checkout@v6
- name: Prepare npm cache
uses: actions/cache@v4
with:
path: ~/.npm
key: npm-${{ runner.os }}-${{ hashFiles('**/package-lock.json') }}
- run: npm ci
この構成には、次の利点があります。
- プルリクエストのテストからキャッシュを保存できない
mainブランチのテスト成功後だけキャッシュを生成できるwrite-onlyにより、キャッシュ生成ジョブ自身は既存キャッシュを復元しない- 保存権限をワークフロー全体へ広げずに済む
キャッシュ生成ジョブでも既存キャッシュを利用したい場合は、write-onlyをwriteへ変更します。
readにするとキャッシュ保存処理は失敗するのか
cache-mode: readのジョブでactions/cacheが保存を試みても、通常はジョブ全体が失敗するわけではありません。
許可されていないキャッシュ操作はスキップされ、次のように扱われます。
- 復元が禁止されている場合はキャッシュミスとして扱われる
- 保存が禁止されている場合は保存処理だけが行われない
- 情報メッセージがログに記録される
- ステップやワークフローはそのまま続行する
有効な設定値は、ランナー上のACTIONS_CACHE_MODE環境変数にも反映されます。想定どおりのモードになっているか調査するときに利用できます。([GitHub Docs][2])
意図をさらに明確にしたい場合は、読み取り専用ジョブでactions/cache/restoreを使用する方法もあります。ただし、cache-mode: readを設定していれば、キャッシュサービス側でも保存は制限されます。
cache-modeを省略した場合の既定値
cache-modeを指定していないワークフローでは、トリガーの信頼度に応じてreadまたはwriteが自動的に適用されます。
| トリガーの区分 | cache-mode省略時の実効値 |
|---|---|
| 信頼されたトリガー | write |
| 低信頼トリガー | read |
GitHubのドキュメントでは、デフォルトブランチのキャッシュへ書き込める信頼されたトリガーとして、主に次のイベントが挙げられています。
pushworkflow_dispatchrepository_dispatchdeleteregistry_packagepage_buildschedule
一方、pull_request_target、issue_comment、workflow_runなど、外部ユーザーの操作や入力の影響を受ける可能性があるイベントは、デフォルトブランチのキャッシュに対して原則読み取り専用になります。([GitHub Docs][3])
既存のワークフローにcache-modeを追加しなくても、従来の安全側の既定動作は継続します。ただし、権限の意図をYAML上で明確にし、再利用ワークフローへの権限伝播を確実に制限するには、明示的な設定が有効です。([GitHub Blog][1])
pull_requestは特別な扱いになる
通常のpull_requestイベントは、pull_request_targetと同じ扱いではありません。
pull_requestで生成されたキャッシュは、refs/pull/.../mergeというプルリクエスト固有のマージ参照にスコープされます。そのキャッシュは、デフォルトブランチや別のプルリクエストから復元できないため、デフォルトブランチのキャッシュ書き込み制限とは別の仕組みで隔離されています。([GitHub Docs][3])
ただし、ワークフローを見ただけで権限を判断しやすくするため、プルリクエストの検証ジョブにはcache-mode: readを明示する運用も有効です。
低信頼イベントにwriteを設定するときの注意点
次のようにpull_request_targetでwriteを明示すると、GitHubが適用する読み取り専用の既定値を上書きします。
on:
pull_request_target:
cache-mode: write
この設定は構文上可能ですが、キャッシュポイズニングの危険性が高まります。GitHub Actionsは、低信頼トリガーにwriteまたはwrite-onlyを明示した場合、警告を表示します。([GitHub Blog][1])
キャッシュポイズニングとは、攻撃者の影響を受けるワークフローが不正なファイルをキャッシュへ保存し、そのキャッシュを後から権限の強いワークフローが復元してしまう攻撃です。復元されたスクリプト、実行ファイル、ビルド成果物などが後続処理で使用されると、意図しないコードが実行される可能性があります。([GitHub Docs][3])
低信頼イベントでキャッシュが必要な場合は、次の構成を優先します。
- 低信頼イベントのジョブは
readにする pushなど信頼できるイベントでキャッシュを更新する- キャッシュ更新ジョブでは外部から渡されたコードや入力を処理しない
- デプロイや署名など重要なジョブでは
noneを検討する - キャッシュ対象にトークン、認証情報、秘密鍵を含めない
readは「安全なキャッシュだけを読む」という意味ではない
cache-mode: readは、そのジョブによるキャッシュ保存を禁止する設定です。復元されるキャッシュの内容が安全であることを保証する機能ではありません。
別のワークフローが不正なキャッシュを保存していれば、readのジョブがそのキャッシュを復元する可能性は残ります。また、GitHub Actionsのキャッシュは署名や内容検証が行われる仕組みではありません。([GitHub Docs][3])
そのため、次のようなジョブではreadではなくnoneが適しています。
- 本番環境へのデプロイ
- コード署名やパッケージ署名
- リリース成果物の最終生成
- キャッシュされた実行ファイルを信用できない処理
- 外部リポジトリ由来のワークフローを呼び出す処理
**保存を禁止したいならread、復元も保存も禁止したいならnone**と判断すると分かりやすくなります。
再利用ワークフローのキャッシュ権限を制限する
再利用ワークフローを呼び出すジョブにも、cache-modeを設定できます。
jobs:
shared-ci:
uses: example-org/shared-workflows/.github/workflows/ci.yml@v1
cache-mode: read
呼び出し側で明示したcache-modeは、呼び出された再利用ワークフローの上限になります。呼び出し側がreadしか許可していないのに、再利用ワークフロー側がwriteを要求すると、ワークフローは開始されず、検証エラーになります。([GitHub Docs][2])
readとwrite-onlyも、単純な強弱関係ではありません。
readは復元だけを許可するwrite-onlyは保存だけを許可する
権限が重ならないため、呼び出し側がread、呼び出し先がwrite-onlyという組み合わせも、上限を超えた要求として扱われます。([GitHub Docs][3])
呼び出し側ではcache-modeを明示する
再利用ワークフローでは、呼び出し側のcache-modeを省略しないことが重要です。
GitHubのドキュメントによると、呼び出しジョブに明示的なcache-modeがなく、低信頼トリガーによる実効既定値だけがreadになっている場合、呼び出された再利用ワークフローがwriteを明示的に要求できるケースがあります。確実に読み取り専用へ制限するには、呼び出しジョブにcache-mode: readを記述します。([GitHub Docs][3])
実際のワークフローを見直す手順
キャッシュを使用しているワークフローを探す
まず、.github/workflows配下からキャッシュ関連の設定を検索します。
git grep -nE 'actions/cache|cache:' -- .github/workflows
actions/cacheだけでなく、actions/setup-nodeやactions/setup-pythonなどのcacheオプションも確認します。
ジョブごとに必要な操作を分類する
各ジョブについて、次の2点を確認します。
- 既存キャッシュを復元する必要があるか
- 新しいキャッシュを保存する必要があるか
判断結果を4つのモードに当てはめます。
| ジョブの役割 | 推奨する設定 |
|---|---|
| テストやLintで既存キャッシュだけ使う | read |
| 信頼できるビルドで復元と保存を行う | write |
| クリーンな状態からキャッシュを生成する | write-only |
| デプロイなどでキャッシュを一切使わない | none |
ワークフロー全体をreadにする
複数のイベントや多数のジョブを含むワークフローでは、まずトップレベルをreadにします。
cache-mode: read
これにより、新しく追加されたジョブが意図せずキャッシュ保存権限を持つことを防ぎやすくなります。
保存が必要なジョブだけ上書きする
信頼できるトリガーで実行されるジョブだけ、writeまたはwrite-onlyへ変更します。
jobs:
build:
cache-mode: write
test:
cache-mode: read
deploy:
cache-mode: none
再利用ワークフローの呼び出し元も確認する
uses:で別のワークフローを呼び出しているジョブには、明示的なcache-modeを設定します。
特に低信頼イベントから呼び出される場合は、既定値に任せずreadまたはnoneを指定します。
よくある設定ミス
複数イベントのワークフロー全体をwriteにする
次の設定では、pushだけでなく、同じワークフローを起動するすべてのイベントにwriteが適用されます。
on:
push:
pull_request_target:
cache-mode: write
pull_request_targetに対する安全側の既定値も上書きされるため、避けるべき構成です。
ワークフロー全体はreadにし、push時だけ実行する特定ジョブをwriteにします。
permissionsだけでキャッシュ権限を制御しようとする
permissions:は主にGITHUB_TOKENの権限を制御する設定です。キャッシュの復元・保存権限は、専用のcache-modeで設定します。
permissions:
contents: read
cache-mode: read
両方を設定することで、リポジトリ操作とキャッシュ操作を個別に最小権限化できます。cache-modeはスコープ付きのキャッシュトークンとしてキャッシュサービス側で強制されます。([GitHub Docs][2])
デプロイジョブをreadにして安心する
readはキャッシュを復元できます。デプロイ処理でキャッシュを一切利用させたくない場合は、noneを指定します。
jobs:
deploy:
cache-mode: none
キャッシュ保存がスキップされたらジョブも失敗すると思う
cache-modeによって禁止された保存処理は、通常はスキップされるだけです。
キャッシュが更新されていないのにワークフローが成功している場合は、ログとACTIONS_CACHE_MODEを確認します。([GitHub Docs][3])
GitHub Enterprise Serverで利用できるか
cache-modeの一般提供が明記されている対象は、GitHub.comの全プランです。GitHub Enterprise Serverについては、2026年9月10日の発表だけでは提供バージョンや時期を確定できません。([GitHub Blog][1])
GitHub Enterprise Serverを利用している場合は、導入済みバージョンのリリースノートとワークフロー構文ドキュメントにcache-modeが掲載されているかを確認してください。
cache-modeは全体をreadにして必要なジョブだけ緩める
GitHub Actionsのキャッシュを読み取り専用にする基本設定は、トップレベルのcache-mode: readです。
そのうえで、ジョブの役割に応じて権限を変更します。
- 復元だけ必要なら
read - 復元と保存が必要なら
write - 保存だけ必要なら
write-only - キャッシュを完全に禁止するなら
none
複数のイベントを扱うワークフローでは、全体をreadにして、信頼できるpushなどで動くキャッシュ更新ジョブだけをwriteまたはwrite-onlyにする構成が適しています。
まず.github/workflows配下のキャッシュ利用箇所を洗い出し、各ジョブに本当に保存権限が必要かを確認してください。特にpull_request_targetなどの低信頼イベントと、再利用ワークフローの呼び出しジョブは、既定値に任せずcache-modeを明示することが重要です。
[1]: https://github.blog/changelog/2026-09-10-control-github-actions-cache-access-with-cache-mode/ “Control GitHub Actions cache access with cache-mode – GitHub Changelog”
[2]: https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-syntax “Workflow syntax for GitHub Actions – GitHub Docs”
[3]: https://docs.github.com/en/actions/reference/dependency-caching-reference “Dependency caching reference – GitHub Docs”

コメント