office840 lab

限定公開サイト構築ノウハウ集

Cloudflare Pages + Cloudflare Access + GitHub による「無料・認証付き・即運用可能」な限定公開サイトの作り方

所要時間: 約 60〜90 分 月額費用: 0 円(50ユーザーまで) 対象: Windows + PowerShell + Git 最終更新: 2026-05-04

1. 概要 - この方式の特徴

独自ドメインのネームサーバーを動かさずに、「特定メンバーだけが閲覧できるWebサイト」 を即座に立ち上げる方式。

採用したスタック

  • Cloudflare Pages - 静的サイトホスティング(CDN付き、無料)
  • Cloudflare Access - メールOTPによる認証ゲート(無料、50ユーザーまで)
  • GitHub プライベートリポジトリ - ソース管理 + 自動デプロイのトリガー

この方式が向いているケース

  • セッション記録・社内資料・取引先向け資料を限定公開したい
  • 独自ドメインを持っているが、Google Workspace 等のメール用途で ネームサーバーを動かせない
  • WordPress を立てるほどでもないが、ベーシック認証では物足りない
  • VPN や IP 制限ではなく、メールアドレスで個別アクセス制御 したい

この方式が向かないケース

  • ログイン後に動的処理(DB更新等)を行いたい → 別途バックエンド必要
  • 不特定多数(数百人〜)に公開したい → 50ユーザー超で有料
  • カスタムドメイン(lab.example.com 等)で運用したい → ネームサーバーを Cloudflare に移管する作業が必要

2. アーキテクチャ全体像

[訪問者] │ ① https://main.{project}.pages.dev/ ▼ [Cloudflare Access] ── ② メールOTP認証 │ ③ 認証OK → Cookie発行(24h) ▼ [Cloudflare Pages] ── ④ 静的サイトを配信 ▲ │ ⑤ git push で自動デプロイ [GitHub プライベートリポジトリ] ▲ │ ⑥ git push origin main [ローカルPC(PowerShell)]

データフロー

  1. 訪問者は固定URL https://main.{project}.pages.dev/ にアクセス
  2. Cloudflare Access がメールアドレス入力を要求
  3. 許可リストに入っているアドレスにのみ6桁コードがメール送信される
  4. コード入力で認証成功 → 24時間有効な認証Cookieが発行される
  5. Pages の静的コンテンツが配信される
  6. コンテンツ更新は git push で自動的に Pages に反映される(1〜2分)

3. 前提条件

必要なアカウント

サービス用途費用
CloudflarePages + Access無料(50ユーザーまで)
GitHubプライベートリポジトリ無料

必要なローカル環境(Windows)

  • Git for Windows (git --version で確認)
  • PowerShell 5.1+ または PowerShell 7
  • テキストエディタ(VSCode 等)

事前に決めておくこと

  • プロジェクト名(英小文字+ハイフン): 例 office840-lab。これが {project}.pages.dev のサブドメインになる
  • 許可するメールアドレスのリスト: 認証で通したいユーザーのメール
  • Zero Trust のチーム名(後で変更不可): 例 office840。これが {team}.cloudflareaccess.com の認証ドメインになる

4. 構築手順(全8ステップ)

Step 1

Cloudflare アカウント作成 + 2FA 有効化

  1. https://dash.cloudflare.com/sign-up でアカウント作成
  2. 確認メールのリンクで本人確認
  3. 右上アイコン → My ProfileAuthentication タブ → Two-Factor Authentication を Enable
  4. Authenticator アプリで QR コードを読み取り、6桁コードで確認
  5. リカバリーコードを必ず保存(パスワードマネージャ推奨)
必須: 認証基盤のアカウントなので 2FA 必須。乗っ取られると全保護が崩れる。
Step 2

Zero Trust 有効化(チーム名設定)

  1. Cloudflare ダッシュボード左メニューから Zero Trust
  2. 初回はチーム名(team domain)入力を要求される → 例 office840
  3. プラン選択画面で Free を選択(50ユーザーまで永久無料)
  4. 支払い情報を登録(Free プランでは課金されないが登録は必須)
注意: チーム名は基本的に変更不可。後悔しない名前を選ぶこと。認証URLは {team}.cloudflareaccess.com となる。

確認: 設定 → 「チーム名とドメイン」で値を控える。

Step 3

GitHub プライベートリポジトリ作成

  1. GitHub にログイン → +New repository
  2. Repository name: {project名} (例: office840-lab)
  3. Visibility: Private 必須
  4. Add a README file はチェックを外しても OK(空リポでも進められる)
  5. Create repository
Tip: プライベート設定でも、Cloudflare Pages は GitHub App 経由で読めるので問題ない。むしろソースコードを公開しないこの構成が安全。
Step 4

ローカルにクローン + サンプルサイト作成 + 初回 push

PowerShell で以下を順次実行する。

4-1. クローン

cd C:\Content_Factory\
git clone https://github.com/{username}/{project}.git
cd {project}

初回のみブラウザで GitHub 認証が走る → Authorize Git Credential Manager をクリック。

4-2. サンプルサイトを作成

最低限の index.htmlstyle.css を作成する。
具体的には以下のような index.html をエディタで作成:

<!DOCTYPE html>
<html lang="ja"><head><meta charset="UTF-8">
<title>{Project Name}</title>
<link rel="stylesheet" href="/style.css"></head>
<body><h1>Hello {Project Name}</h1></body></html>

4-3. Git 設定 + 初回 push

# 必要なら初回のみ user 設定(--global を付けないとこのリポだけに適用)
git config user.name "{your-github-username}"
git config user.email "{your-email}"

git add .
git commit -m "feat: 初期サンプルサイト"
git push -u origin main
豆知識: Windows で warning: in the working copy of '...', LF will be replaced by CRLF という警告は無視して OK。
Step 5

Cloudflare Pages プロジェクト作成(GitHub 連携)

  1. Cloudflare ダッシュボード → コンピュートWorkers & Pages
  2. 右上 アプリケーションを作成する → 画面下部の「Pages を導入しようとお考えですか? 始める」をクリック
  3. 既存の Git リポジトリをインポートする始める
  4. GitHub に接続 → Only select repositories{project} のみ許可 → Install & Authorize
  5. リポジトリ {project} を選択 → セットアップの開始
  6. ビルド設定: フレームワーク なし, ビルドコマンド 空欄, 出力ディレクトリ 空欄(静的HTMLの場合)
  7. 保存してデプロイする → 1〜2分で完了
Tip: 「Only select repositories」を選んで対象リポだけ許可するのが最小権限の原則。「All repositories」は避ける。

完了すると https://{project}.pages.dev/ でサンプルサイトが 誰でも閲覧可能な状態 で公開される。次のステップで保護をかける。

Step 6

Pages の「Access ポリシー」を有効化(最重要)

このステップが 本ノウハウ集の最大のポイント。Access Application を手動で作るより、Pages 側から自動連携する方が確実。

  1. Cloudflare → Workers & Pages{project} をクリック
  2. 上部タブの 設定 → 左サブメニュー 一般
  3. 「Access ポリシー」項目の 有効にする をクリック

これだけで以下が 自動的に 行われる:

  • Access Application が新規作成される(名前: {project} - Cloudflare Pages)
  • 宛先が *.{project}.pages.dev に自動設定される(プレビュー含むワイルドカード)
  • 初期ポリシー Allow Members - Cloudflare Pages が作成され、現在ログイン中のメールアドレスが許可リストに追加される
なぜ自動連携ルートが正解か: 直接 Access コントロール → アプリケーション作成からだと、宛先選択ドロップダウンに pages.dev が出てこず詰まる。Pages 側からの自動連携なら正しい宛先で生成される。
注意: 説明文には「プレビューデプロイメントへのアクセスを制御します」と書かれているが、実際は *.{project}.pages.dev 全体(プレビュー + 一部の固定エイリアス)が保護対象。本番URL {project}.pages.dev (apex) だけは別途追加が必要(後述)。
Step 7

許可メールアドレスを Access ポリシーに登録

初期状態では現在ログインしているメールしか許可されていない。他メンバーを追加する場合のみこの手順を行う。

  1. Cloudflare → Zero TrustAccess コントロールアプリケーション
  2. {project} - Cloudflare Pages をクリック
  3. ポリシー タブ → Allow Members - Cloudflare Pages をクリック
  4. 「ポリシールール」の Include で Selector: Emails, Value にメールアドレスを列挙
  5. 保存
注意: Cloudflare の一時障害で「Maximum call stack size exceeded」エラーが出ることがある。出たら時間を置いて再試行するか、Selector を Emails 以外(認証方法=SMS、Country 等)に切り替えて保存できるか試す(後述 6-6 参照)。

複数のメールアドレスを許可する場合の代表例:

  • 個別列挙: alice@example.com, bob@example.com ...
  • ドメイン全許可: Selector を Emails ending in にして値を @example.com

認証方法(Selector)の選び方

Selector意味使いどころ
Emailsメールアドレス完全一致個別ユーザー指定の基本形
Emails ending inメールドメインで一括同じ会社のメンバー全員
認証方法 (SMS)SMSコード認証経由のみ許可携帯番号認証を強制したい時、メールルール保存で call stack エラーが出る時の代替
Countryアクセス元の国日本国内のみ等の地域制限
IP rangesIPアドレス範囲オフィスIPのみ等
複数ポリシーの組み合わせ: 1つのアプリケーションに複数の Allow ポリシーを並べると OR 評価(どれか1つでも合致すれば通過)。例: ポリシー1=「SMS 認証」+ ポリシー2=「Emails 個別」併用すると、どちらの方式でもアクセス可能になる。
セキュリティ注意: 「認証方法=SMS」だけのポリシーは、任意の電話番号でアクセス可能になる場合があるので、IP制限・国制限・電話番号許可リストなどを併用すること。
Step 8

動作確認

  1. ブラウザで シークレットウィンドウ(Ctrl+Shift+N) を開く
  2. https://main.{project}.pages.dev/ にアクセス
  3. Cloudflare Access の認証画面が表示される
  4. 許可リストのメールアドレスを入力 → Send me a code
  5. メールに6桁コードが届く → 入力 → Sign in
  6. サンプルサイトが表示されたら成功
Tip: 通常ウィンドウだとログイン状態の Cookie で認証画面がスキップされるので、認証ゲートが効いているかは 必ずシークレットウィンドウ で確認すること。

5. URL構造の理解

Cloudflare Pages は1つのプロジェクトに対して複数の URL を生成する。各 URL の特性を把握しておくと運用で迷わない。

URL用途更新タイミング保護対象
{project}.pages.dev 本番(apex) main ブランチ最新 未保護(後述)
main.{project}.pages.dev 固定エイリアス(推奨) main ブランチ最新 保護対象 ✓
{deploy-id}.{project}.pages.dev 各デプロイのプレビュー 各コミット時点で固定 保護対象 ✓
{branch}.{project}.pages.dev ブランチプレビュー そのブランチ最新 保護対象 ✓
重要: Cloudflare Access の宛先 *.{project}.pages.devサブドメインのみ マッチする。apex の {project}.pages.dev(ドット無し) はワイルドカードで保護されない。本番URLを保護するには別途追加 Application が必要。

運用上の推奨URL

共有メンバーには main.{project}.pages.dev を伝えるのが正解。

  • 固定で覚えやすい
  • 常に最新コンテンツを反映
  • Access の保護対象内
  • 本番 apex を共有しないことで未保護リスクを回避できる

6. ハマりポイントと回避策

6-1. 独自ドメインのネームサーバー移管に踏み込まない

Cloudflare で独自ドメインを使うには通常ネームサーバーを Cloudflare に向ける必要がある。しかし Google Workspace 等でメールを使っている場合、MX/SPF/DKIM/DMARC を全て Cloudflare 側にコピーする必要があり、ミスるとメール不達になる。

回避策: 最初は *.pages.dev 標準ドメインで運用し、必要になってからサブドメイン委任で対応する。

6-2. Access Application を手動作成しない

Access コントロール → アプリケーション → 新規作成から pages.dev を保護しようとすると、宛先ドロップダウンに pages.dev が出てこない。「カスタム入力に切り替える」で URL を直接入れても、ポリシー保存時に内部バリデーションで失敗する場合がある。

回避策: Pages プロジェクトの「設定 → 一般 → Access ポリシー → 有効にする」を使う。Application は自動生成される。

6-3. ワイルドカードは apex をカバーしない

*.example.pages.devfoo.example.pages.dev はマッチするが、ドット無しの example.pages.dev にはマッチしない。

回避策(後日対応):

  • 運用回避(推奨): 共有URLには必ず main.{project}.pages.dev を使う。本番 apex は誰にも教えない。
  • 追加保護: 別途 Application を新規作成し、宛先を {project}.pages.dev(サブドメイン空欄) にして同じポリシーを割り当てる。Cloudflare サービスが安定している時間帯に実施。

6-4. プレビューURLのIDはコミットハッシュではない(罠)

各デプロイの一意URL {deploy-id}.{project}.pages.dev{deploy-id} 部分は、git のコミットハッシュとは別物。Cloudflare Pages 側で独立に発行される8文字のIDで、コミット aaac947 に対応するデプロイID が 45694e84 といった具合に紐付かない。

正しい確認方法: Pages ダッシュボード → デプロイ タブで該当コミットの行の「デプロイ」列に表示される xxxxxxxx.pages.dev をコピー。git log のハッシュ8文字でURL組み立てしようとすると 100% Nothing is here yet になる。

回避策: 共有URLには必ず main.{project}.pages.dev を使い、デプロイ別URLが必要な時だけ Pages ダッシュボードで取得する。

6-5. CDNエイリアスの伝播待ち&ローカルDNSキャッシュの罠

新しい push 直後、main.{project}.pages.dev が「Nothing is here yet」を返すことがある。原因は2つあり区別が必要。

原因A: Cloudflare 側の CDN 伝播待ち(数分〜十数分)

  • 5〜15分待てば解消することが多い
  • 同じデプロイの {deploy-id}.{project}.pages.dev で先行確認可能

原因B: ローカル端末の DNS キャッシュ(意外と多い)

長時間「Nothing is here yet」が続く場合、原因は Cloudflare ではなく 自分のPCのDNSキャッシュが古いCDNノードを参照していることがある。確認方法は 別ネットワーク(スマホの4G/5G、テザリング)からアクセス。別回線で表示できれば、自分のPCのDNS問題で確定。

PC側の解決手順:

  1. PowerShell(管理者)で ipconfig /flushdns
  2. ブラウザのDNSキャッシュもクリア
    • Chrome/Edge: chrome://net-internals/#dns → Clear host cache
  3. それでもダメなら端末の DNS を一時的に 1.1.1.1(Cloudflare) や 8.8.8.8(Google) に変更
  4. 最終的には DNS TTL が切れる(5〜60分)のを待てば解消
切り分けの鉄則: 「Cloudflare障害」と「ローカルDNS問題」を見分けるため、必ず別ネットワークで試すこと。PC(Wi-Fi)でNGでも、スマホ(モバイル回線)でOKなら100%自分のPC側の問題。

6-6. Cloudflare ダッシュボードの一時障害&認証方法スイッチによる回避

「Maximum call stack size exceeded」「エラーが発生しました。リロードしてください」といった汎用エラーが出ることがある。これは Cloudflare 側の一時障害(ダッシュボード上部に黄色バナーで表示されることが多い)。

回避策(優先順):

  1. 認証方法を切り替える(実証済み): ポリシー作成時、Selector に Emails を選んで保存しようとして call stack エラーが発生する場合、Selector を 認証方法 (Authentication Method) = SMSCountry 等に変えると保存できるケースがある。これは Cloudflare ダッシュボードの一時的な内部バグ回避策。
  2. 30分〜数時間待ってから再試行(障害復旧待ち)
  3. cloudflarestatus.com で障害状況確認
実例: 本サイト構築時、メールOTPで Emails ルールを保存しようとすると call stack エラー多発 → 認証方法 SMS の別ポリシー作成に切り替えたら成功 → 後ほど Emails ポリシーも併用可能になった。

6-7. 認証ゲートの動作確認は必ずシークレットウィンドウで

通常ウィンドウだと既存の認証Cookieで素通りしてしまい、本当に保護されているか分からない。

回避策: Ctrl+Shift+N でシークレットウィンドウを開いて確認。

6-8. ポリシー編集が壊れた場合の「現状維持」判断

Web UI で「メール許可リスト編集」が異常終了する状態が続くと、つい代替ポリシーを作って迂回したくなるが、これは セキュリティホールを生む典型パターン。冷静に対処すること。

原則(優先順):

  1. 既存ポリシーが動作しているなら、削除せずそのまま使う。動いている設定を壊さないことが最優先(ロックアウト防止)。
  2. 暫定で別認証方法のポリシーを作る回避策は推奨しない。「認証方法 = SMS」「認証方法 = MFA」のような Selector 単独 Allow ポリシーは、結果的に「MFA を完了した誰でも通れる」抜け穴になりやすい。
  3. メンバー追加・変更が不要なら現状維持で問題なし。個人利用や少人数運用なら、初期作成時の許可リストのまま長期運用できる。

メンバー追加が必要になった時の試行順:

  1. 別ブラウザで試す(Chrome→Edge / Firefox など)。キャッシュ・拡張機能の影響を排除
  2. シークレットウィンドウで Cloudflare ダッシュボードにログインし直して編集
  3. ブラウザのCookie・キャッシュ完全クリア → 再ログイン
  4. 別PCで試す
  5. 数時間〜1日待つ(Cloudflare ダッシュボードの一時的なロールアウト不具合の可能性)
  6. Cloudflare API + cURL で policies エンドポイント直接操作
  7. Cloudflare サポートに問い合わせ
避けるべき罠: Web UI で連続リトライすると、ダッシュボードの操作履歴・状態管理が壊れて状況が悪化することがある。1回ダメだったら時間を置く。

6-9. ロックアウト対策(メールアカウント保護)

このサイトの認証は「メールOTP」一本になることが多い。そのメールアカウントを失う = サイトに永久にログイン不能になる。

事前対策(必須):

  • 許可リストのメールアカウントに 2FA を有効化(Google Workspace なら2段階認証)
  • 2FA リカバリーコードを パスワードマネージャに保管
  • Cloudflare アカウント自体の 2FA + リカバリーコード も同様に保管
  • 可能なら、緊急用の管理者として別 Cloudflare アカウントを Pages プロジェクトに招待しておく(冗長化)

7. 運用Tips

7-1. コンテンツ追加・更新

PowerShell でローカルリポジトリで作業:

cd C:\Content_Factory\{project}
# (ファイルを編集 or 追加)
git add .
git commit -m "feat: {変更内容}"
git push origin main

push 後、約1〜2分で main.{project}.pages.dev に反映。エイリアス更新の遅延がある場合はさらに数分。

7-2. メンバー追加

Zero Trust → Access コントロール → アプリケーション → {project} - Cloudflare Pages → ポリシー → Allow Members - Cloudflare Pages → ルールの Emails 値にアドレス追加 → 保存

7-3. メンバー削除

同じ画面で Emails リストから削除 → 保存。即座に反映され、対象メンバーは認証通過できなくなる。

7-4. アクセスログ監査

Zero Trust → ログ(Logs) → Access でログイン履歴を確認可能。誰がいつ認証したかすべて記録される。

7-5. セッション期間の調整

デフォルトで認証成功後24時間は Cookie で再認証不要。短くしたい場合は Application の「セッション期間」を変更。

7-6. JSONL/Markdown からの自動HTML生成

Claude Code のセッションログ等、JSONL/Markdown 形式の素材を HTML に変換するスクリプトを用意しておくと運用が楽。本リポジトリの scripts/build_edge_ai_page.py が参考実装。

8. 費用

サービス無料枠超過時
Cloudflare Pages無制限のリクエスト・帯域、500ビルド/月$5/月でビルド枠拡張
Cloudflare Access(Free)50ユーザーまで$3/ユーザー/月(Standardプラン)
GitHub Private Repo無制限(Free Plan)無料のまま

つまり、50ユーザー以下の限定公開なら永久無料

9. クイックチェックリスト

次回別サイトを作るときに、このリストだけ見れば再現可能。

#項目確認
1Cloudflare アカウント作成 + 2FA + リカバリーコード保存
2Zero Trust 有効化 + チーム名決定
3GitHub プライベートリポジトリ作成
4ローカルにクローン → サンプル index.html 作成 → push
5Cloudflare Pages プロジェクト作成 → GitHub 連携 → デプロイ確認
6Pages 「設定 → 一般 → Access ポリシー → 有効にする」
7許可メールアドレス追加(複数メンバー時)。エラー出る場合は Selector=認証方法(SMS) で代替ポリシー作成
8シークレットウィンドウで main.{project}.pages.dev 動作確認
9共有用URLを main.{project}.pages.dev でメンバー周知
10(任意)apex URL の追加保護 Application 作成
11許可メールアカウントの 2FA・リカバリーコード保管(ロックアウト対策)

10. 参考リソース

← セッション一覧に戻る