CLOUDFLARE_API_TOKEN を GitHub の Repository Secrets に貼って、wrangler deploy を CI から叩く — Cloudflare Workers の標準的なデプロイ構成です。これ自体は機能しますが、実運用で気になる点が 3 つあります。
- API Token の有効期限を切らないと、漏れたら無期限に使われ続ける
- 切ると今度は定期的にローテーションする運用が発生する
- どの Workflow がいつ Token を使ったかの監査が、Cloudflare 側のログでしか追えない
GitHub Actions の OIDC (OpenID Connect) 連携を使うと、CI 実行ごとに「そのジョブだけに発行される短命 access token」を Cloudflare 側で発行できるので、長寿命 Secret を持たずに済みます。本記事では、Workers のデプロイを OIDC 化する最小構成をまとめます。
何が嬉しいのか — シークレットレスの実利
OIDC 連携は地味に効くポイントが揃っています。
- GitHub Secrets に Cloudflare の長寿命 Token を置かない。漏洩リスクが「ジョブ実行中の数分」に限定される
- Token の発行先は Workflow の identity に紐づく。「main ブランチの push からのみ」「特定 environment に対してのみ」など、claim 単位で絞れる
- Cloudflare 側の Audit Logs に GitHub の Workflow run ID が残る。誰のどの PR のデプロイかが事後追跡可能
「個人開発で Secret を 1 つ持つだけ」というケースでも、ローテーション忘れ問題が消えるだけで導入価値があります。
全体構成
GitHub Actions ─(OIDC token)─▶ Cloudflare API
│
▼
短命 access token を発行
│
▼
wrangler deploy
actions/configure-aws-credentials 的な「OIDC を直接 STS に投げる」パターンと同じ発想です。Cloudflare 側は Cloud Connectors / Account API Tokens の OIDC trust を信頼ポリシーとして登録します。
1. Cloudflare 側で OIDC trust を作る
Cloudflare Dashboard の「Manage Account → API Tokens → Create Token」から、OIDC Federation 用のテンプレートを使います (2026-04 時点で GA)。要点:
- Issuer:
https://token.actions.githubusercontent.com - Audience:
cloudflare(任意の文字列で OK、Workflow 側と一致させる) - Subject claim で絞り込み:
repo:<owner>/<repo>:ref:refs/heads/mainのように、ブランチ単位 / environment 単位で絞る - Permissions:
Account → Workers Scripts → Edit/Zone → DNS → Editなど、必要な最小権限のみ
設定が終わると、account_id と role_arn 相当の oidc_role_id が払い出されます。これを Workflow に渡します。
2. Workflow を書く
Workflow 全体です。permissions: id-token: write を付け忘れると OIDC token が発行されないので、ここを最初に確認。
# .github/workflows/deploy.yml
name: deploy
on:
push:
branches: [main]
workflow_dispatch:
permissions:
id-token: write # OIDC token 発行に必須
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v5
- uses: actions/setup-node@v5
with:
node-version: "22"
- run: npm ci
# OIDC token を取得 → Cloudflare の短命 access token に交換
- name: Exchange OIDC for Cloudflare token
id: cf
uses: cloudflare/oidc-auth-action@v1
with:
account_id: ${{ vars.CF_ACCOUNT_ID }}
oidc_role_id: ${{ vars.CF_OIDC_ROLE_ID }}
audience: cloudflare
# 払い出された短命トークンを wrangler に渡す
- name: Deploy
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ steps.cf.outputs.api_token }}
accountId: ${{ vars.CF_ACCOUNT_ID }}
command: deploy
vars.* は Repository Variables (Secrets ではなく非機密な設定値)。account_id と oidc_role_id はリーク しても単独では悪用されないので、Variables に置いて構いません。
3. ブランチ・環境で claim を絞る
OIDC の旨味は Subject claim を絞れるところです。Cloudflare 側の trust policy で次のような縛りを入れておきます。
| 縛り例 | Subject claim |
|---|---|
| main ブランチからの push のみ | repo:r43lab/site:ref:refs/heads/main |
production environment のジョブのみ | repo:r43lab/site:environment:production |
| Pull Request からは拒否 | pull_request を含む subject を許可しない |
environment: production を Workflow に書き、Cloudflare 側で environment:production claim を要求すると、PR からは production にデプロイできない という制約が、Cloudflare 側でも担保されます。GitHub の environment protection rule と二重に効くので、誤デプロイがほぼ起きません。
4. Preview 用にもう 1 つ trust を作る
「main は production、PR は preview」とよくあるので、preview 用にもう 1 つ trust を発行しておきます。
- Subject:
repo:<owner>/<repo>:pull_request - Permissions: Workers Scripts Edit + Preview Deployments のみ
- Account: 同じ
PR 側の Workflow:
on:
pull_request:
permissions:
id-token: write
contents: read
pull-requests: write
jobs:
preview:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/setup-node@v5
with: { node-version: "22" }
- run: npm ci
- id: cf
uses: cloudflare/oidc-auth-action@v1
with:
account_id: ${{ vars.CF_ACCOUNT_ID }}
oidc_role_id: ${{ vars.CF_OIDC_ROLE_ID_PREVIEW }}
audience: cloudflare
- uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ steps.cf.outputs.api_token }}
accountId: ${{ vars.CF_ACCOUNT_ID }}
command: versions upload
versions upload で preview deployment が作られ、wrangler-action が PR コメントに preview URL を貼ってくれます。本番権限を持たない trust から発行しているので、PR の Workflow が暴走しても production が書き換わらないのがポイントです。
落とし穴 / ハマりポイント
permissions: id-token: writeを付け忘れる。OIDC token が発行されず、Could not fetch OIDC tokenで落ちる。最初に踏むやつ- Subject claim を絞らずに trust を作ると、別リポジトリの Workflow からも token 発行可能になる。最低
repo:<owner>/<repo>:*レベルの絞りは必須 audienceのミスマッチ。Cloudflare trust 側と Workflow 側でaudienceを同じ文字列にしないと交換に失敗するenvironment:をジョブに書かないと environment claim が乗らない。environment 縛りで絞っているのに「environment claim が無い」で落ちることがあるvars.CF_ACCOUNT_IDを Secrets に入れるか Variables に入れるか。漏れても OIDC trust が無ければ何もできないので Variables で OK だが、組織ポリシーで決まっているならそれに従う
ざっくり比較: 旧来の長寿命 Token との差
| API Token in Secrets | OIDC 連携 | |
|---|---|---|
| 漏洩時の影響時間 | 失効までずっと | ジョブ実行中の数分 |
| ローテーション | 手動運用必要 | 不要 |
| ブランチ単位の絞り | 不可 | claim で可能 |
| Audit log | Cloudflare 側のみ | GitHub run ID 紐付き |
| 初期設定の手間 | Secret 1 個コピペ | trust policy 1 個作成 |
初期設定の手間は数分多いだけで、運用フェーズに入ったあとの楽さが段違いです。
まとめ
GitHub Actions の OIDC を Cloudflare の短命 token に交換する構成は、CLOUDFLARE_API_TOKEN を貼っておく旧来構成の上位互換です。
- Secret ローテーションが消える
- ブランチ・環境で claim を絞れる
- Audit log に Workflow run ID が残る
個人開発でも、main / preview の trust を 2 つ作っておくだけで「PR の Workflow が production を壊す事故」が構造的に起きなくなります。Workers / Pages / R2 / D1 すべて同じ仕組みで連携できるので、Cloudflare に乗っているプロダクトは早めに乗り換える価値があります。