SYNON 記事 Cloudflare・インフラ

Cloudflare Workers / D1 / R2 / KV / Vectorize

社内申請システム 設置手順書

対象: 情報システム部・開発担当所要: 半日第 1 版 — 2026年8月15日

0. この手順書について

システム全体構成
システム全体構成

Cloudflare 上に社内申請システムを新規設置する手順です。上から順に実行すれば 完了します。途中で判断を求められる箇所には「★判断」と書いてあります。

作業を始める前に、次の 3 点を確認してください。

なぜ有料プランが必要か 無料プランは 1 日 10 万リクエストかつ 1 リクエストあたり CPU 時間 10ms の制限が あり、50 名規模の社内システムでも上限に触れます。月 $5 で全サービスの 制限が実用水準まで緩和されるため、ここは節約する箇所ではありません。

設置の全体像

設置フロー
設置フロー

手順 内容 目安 自動化
1 事前準備(API トークン発行) 30分 手動
2 開発コンテナの起動 30分 ほぼ自動
3 Cloudflare リソースの一括作成 5分 全自動
4 Cloudflare Access の設定 30分 手動
5 初回デプロイ 15分 ほぼ自動
6 GitHub 自動デプロイの設定 30分 手動
7 初期データの登録 1〜2時間 手動
8 設置後の確認 30分 手動

1. 事前準備

1-1. Account ID を確認する

Cloudflare ダッシュボードにログインし、右下または任意のドメイン概要ページに 表示される 32 桁の Account ID を控えます。

1-2. API トークンを発行する

マイプロフィールAPI トークントークンを作成カスタムトークンを作成

★判断: 権限は必要最小限にします。以下を過不足なく設定してください。

種別 リソース 権限
アカウント Workers スクリプト 編集
アカウント Workers KV ストレージ 編集
アカウント Workers R2 ストレージ 編集
アカウント D1 編集
アカウント Vectorize 編集
アカウント Workers AI 読み取り
アカウント Secrets Store 編集
アカウント アカウント設定 読み取り

重要: 表示されたトークン文字列はこの画面でしか見られません。 パスワード管理ツールに保存してください。紛失した場合は再発行になります。 また、このトークンは本番環境を破壊できる権限を持ちます。Slack やメールで 平文で送らないでください。

1-3. リポジトリを配置して .env を作る

unzip cf-internal-app-starter.zip
cd cf-internal-app-starter

cp .env.example .env
cp .dev.vars.example .dev.vars

.env を開いて、1-1 と 1-2 で取得した値を記入します。

CLOUDFLARE_API_TOKEN=(1-2 で発行したトークン)
CLOUDFLARE_ACCOUNT_ID=(1-1 で確認した 32 桁)
ANTHROPIC_API_KEY=(Claude で開発する場合のみ。空でも可)

.env.dev.vars.gitignore 済みです。Git にコミットされません。


2. 開発コンテナの起動

docker compose up -d
docker compose exec dev bash

以降のコマンドはすべてコンテナの中で実行します。

コンテナには Node 22 / Wrangler / Claude Code / jq / sqlite3 が入っています。 作業者の PC に Node をインストールする必要はありません。

2-1. ローカルで動作確認する

本番に触る前に、手元で動くことを確認します。

npm run db:migrate:local    # ローカル DB にテーブルを作る
npm run db:seed:local       # テストデータを入れる
npm run dev

ブラウザで http://localhost:5173 を開き、申請一覧が表示されれば成功です。

ローカルでは DEV_BYPASS_AUTH=true により認証が迂回され、開発用ユーザーで ログインした状態になります。この設定は APP_ENV=production では無効化される よう二重に条件が入っており、本番で認証がすり抜けることはありません (自動テストで検証済み)。

Ctrl+C で停止します。


3. Cloudflare リソースの一括作成

ここがこの手順書で最も自動化されている部分です。1 コマンドで完了します。

npm run bootstrap

実行されること:

  1. D1 データベース synon-internal-db を作成 → ID を取得
  2. R2 バケット synon-internal-files を作成
  3. KV ネームスペース synon-internal-cache を作成 → ID を取得
  4. Vectorize インデックス synon-internal-docs(768次元 / cosine)を作成
  5. Vectorize のメタデータインデックス(category)を作成
  6. Secrets Store synon-internal-secrets を作成 → ID を取得
  7. 取得した ID を wrangler.jsonc に自動で書き戻す
  8. 本番 D1 にテーブルを作成(マイグレーション適用)

このスクリプトは冪等です。既に存在するリソースはスキップして ID の取得だけ 行うため、途中で失敗しても安心して再実行できます。

3-1. うまくいかないとき

症状 原因と対処
CLOUDFLARE_API_TOKEN が未設定です .env を作った後にコンテナを起動し直す(docker compose down && docker compose up -d
D1 の ID を取得できませんでした トークンに D1 の編集権限が無い。1-2 の表を再確認
Secrets Store の ID を自動取得できませんでした ダッシュボードの Secrets Store 画面で ID を確認し、wrangler.jsonc<SECRETS_STORE_ID> を手で置き換える(通知機能を使わないなら後回しで可)

3-2. ステージング環境も作る場合

★判断: 本番と分けた検証環境が必要なら、続けて実行します。50 名規模で 変更頻度が低い場合は、本番のみで運用しても構いません。

ENV_SUFFIX=-staging npm run bootstrap

4. Cloudflare Access の設定

この手順を飛ばすと、システムが社外に公開されたままになります。必ず実施してください。

進行中の AWS Client VPN 移行案件と同じ ID 基盤に載せるため、認証方式は 既存方針(会社メールへのワンタイム PIN + WARP 端末限定)に揃えます。

4-1. アプリケーションを作成する

Zero TrustAccessApplicationsAdd an applicationSelf-hosted

項目 設定値
Application name 社内申請システム
Session Duration 24 時間
Application domain この Worker のルート(synon-internal-app.<サブドメイン>.workers.dev または独自ルート)

4-2. ポリシーを設定する

項目 設定値
Policy name 社員のみ
Action Allow
Include Emails ending in@synon.co.jp

★判断: 部署単位でアクセスを絞りたい場合は、ここで Emails に個別の アドレスを列挙するか、将来 IdP 連携したうえでグループ条件を使います。 初期導入では全社員許可(上記)で始め、必要が出てから絞るのが実務的です。

4-3. AUD タグを設定ファイルに貼る

アプリケーション作成後、Overview タブに表示される Application Audience (AUD) Tag(64 桁の16進文字列)をコピーします。

wrangler.jsonc を開き、<ACCESS_AUD_TAG> を置き換えます。

"vars": {
  "ACCESS_TEAM_DOMAIN": "https://synon.cloudflareaccess.com",
  "ACCESS_AUD": "3f7a9c...(ここに貼る)",
  "DEV_BYPASS_AUTH": "false",
  "APP_ENV": "production"
},

ACCESS_TEAM_DOMAIN も自社のチームドメインに合わせてください (Zero TrustSettingsCustom Pages などで確認できます)。

認証の流れ
認証の流れ

なぜ Worker 側にも AUD が要るのか Access をルートに紐付けても、workers.dev のアドレスを直接叩かれる経路が 残り得ます。そのため Worker 側でも JWT の署名・発行元・宛先(AUD)を 再検証しています。AUD タグが未設定・誤りだと全ユーザーが 401 になります

4-4. 通知用の Webhook を登録する(任意)

Slack 通知を使う場合のみ。

npx wrangler secrets-store secret create <STORE_ID> \
  --name SLACK_WEBHOOK_URL --scopes workers --remote

対話的に値の入力を求められるので、Slack の Incoming Webhook URL を貼ります。

Secrets Store に置いた値は、再デプロイなしで更新できます。Webhook を 差し替えるときはこのコマンドを実行するだけです。


5. 初回デプロイ

npm run deploy

フロントエンドをビルドし、Worker と一緒に配信します。完了すると URL が 表示されます。

5-1. 動作確認

# 認証不要の死活監視エンドポイント
curl https://<デプロイされたURL>/api/health
# → {"ok":true,"env":"production","time":"..."}

続いてブラウザで URL を開きます。Cloudflare Access のログイン画面が出て、 会社メールに届いた PIN を入力すると申請一覧が表示されれば設置成功です。

5-2. うまくいかないとき

症状 原因と対処
ログイン画面が出ずに直接画面が表示される Access アプリのドメイン指定が Worker の URL と一致していない
ログイン後に画面が真っ白/401 が出る ACCESS_AUD または ACCESS_TEAM_DOMAIN の設定誤り。4-3 を再確認
「規程検索」だけ 503 エラー Vectorize / Workers AI の設定。他の機能には影響しないので後回しで可

6. GitHub 自動デプロイの設定

以降の改修を git push だけで反映できるようにします。

6-1. リポジトリに登録するもの

SettingsSecrets and variablesActions

Secrets(秘匿値)

名前
CLOUDFLARE_API_TOKEN 1-2 で発行したトークン
CLOUDFLARE_ACCOUNT_ID 1-1 で確認した ID

Variables(非秘匿値)

名前
PRODUCTION_URL https://<本番URL>(スモークテスト用。未設定でも動作します)

6-2. 本番デプロイに承認を挟む

★判断: ここは省略できますが、強く推奨します。

SettingsEnvironmentsNew environment → 名前を productionRequired reviewers に情シス担当者を追加

これにより、main への push は自動検証まで進んだあと人間の承認待ちで停止 します。承認して初めて本番に出ます。

6-3. パイプラインの動き

CI/CD パイプライン
CI/CD パイプライン

ブランチ 動作
プルリクエスト 型チェック・テスト・ビルド検証のみ。本番には出ない
staging 検証 → ステージング環境へ自動反映
main 検証 → 人間の承認 → DB移行 → デプロイ → スモークテスト → 失敗時は自動ロールバック

注意: 自動ロールバックが戻すのはアプリケーションのコードだけです。 データベースのスキーマ変更は自動では戻りません。テーブル定義を変える改修は、 必ずステージングで先に試してください。


7. 初期データの登録

7-1. 利用者を登録する

利用者は事前登録が不要です。 Access で認証が通った人が初めてアクセスした 時点で、自動的に一般利用者として登録されます。

登録が必要なのは権限を持つ人だけです。

npx wrangler d1 execute DB --remote --env="" --command "
INSERT INTO users (email, display_name, department, role) VALUES
  ('mtanaka@synon.co.jp', '田中 実',   '情報システム部', 'admin'),
  ('(部門長のアドレス)', '(氏名)',   '(部署)',       'approver')
ON CONFLICT(email) DO UPDATE SET role = excluded.role;
"
role できること
member 申請の作成・提出・自分の申請の閲覧(既定値)
approver 上記 + 自分に割り当てられた申請の決裁
admin 上記 + 設定変更・全添付ファイルの閲覧

7-2. 承認ルートを設定する

金額に応じて承認者を自動で割り当てる設定です。この設定は画面から変更でき、 システム改修は不要です。

curl -X PUT https://<本番URL>/api/settings/approval-rules \
  -H "Content-Type: application/json" \
  -H "CF-Access-Client-Id: ..." \
  -d '[
    {"threshold": 100000, "approver": "(10万円以上の承認者)"},
    {"threshold": 0,      "approver": "(10万円未満の承認者)"}
  ]'

threshold は「この金額以上ならこの人」という下限値です。降順に評価されます。

7-3. 社内規程を登録する

規程検索で引けるようにする文書を登録します。1 件ずつ API で投入します。

curl -X POST https://<本番URL>/api/search/documents \
  -H "Content-Type: application/json" \
  -d '{
    "id": "doc-expense-rule",
    "title": "旅費精算規程",
    "content": "(規程の本文をここに)",
    "category": "rule"
  }'

★判断: 長い規程は章ごとに分割して登録すると検索精度が上がります。ただし 分割数だけ Vectorize の使用量が増えます(無料枠は約 1 万 3 千件相当)。 まずは規程 1 本 = 1 件で登録し、精度が足りなければ分割してください。


8. 設置後の確認

以下をすべてチェックして完了です。

機能確認

運用準備

最重要: Zero Trust Free プランは 50 ユーザーまでです。シートは認証イベントで 消費され、手動で削除するまで保持されます。退職者の回収を運用に組み込まないと、 実在の人数より早く上限に達し、51 人目のログインがブロックされます。 これは進行中の VPN 移行案件と共通の論点です。


付録 A. よく使うコマンド

すべてコンテナ内(docker compose exec dev bash)で実行します。

npm run dev                 # ローカル開発サーバー起動
npm run typecheck           # 型チェック
npm test                    # 自動テスト
npm run deploy              # 本番へ手動デプロイ
npm run deploy:staging      # ステージングへデプロイ
npm run tail                # 本番ログをリアルタイム表示
npm run rollback            # 前バージョンへ戻す

# 本番 DB に直接クエリを投げる(参照は安全、更新は慎重に)
npx wrangler d1 execute DB --remote --env="" --command "SELECT COUNT(*) FROM requests"

付録 B. 設置作業の記録欄

項目
設置実施日
実施者
Cloudflare Account ID
本番 URL
Access チームドメイン
Access AUD タグ
API トークンの保管場所
GitHub リポジトリ URL
本番デプロイ承認者