限定公開サイト構築ノウハウ集
Cloudflare Pages + Cloudflare Access + GitHub による「無料・認証付き・即運用可能」な限定公開サイトの作り方
1. 概要 - この方式の特徴
独自ドメインのネームサーバーを動かさずに、「特定メンバーだけが閲覧できるWebサイト」 を即座に立ち上げる方式。
採用したスタック
- Cloudflare Pages - 静的サイトホスティング(CDN付き、無料)
- Cloudflare Access - メールOTPによる認証ゲート(無料、50ユーザーまで)
- GitHub プライベートリポジトリ - ソース管理 + 自動デプロイのトリガー
この方式が向いているケース
- セッション記録・社内資料・取引先向け資料を限定公開したい
- 独自ドメインを持っているが、Google Workspace 等のメール用途で ネームサーバーを動かせない
- WordPress を立てるほどでもないが、ベーシック認証では物足りない
- VPN や IP 制限ではなく、メールアドレスで個別アクセス制御 したい
この方式が向かないケース
- ログイン後に動的処理(DB更新等)を行いたい → 別途バックエンド必要
- 不特定多数(数百人〜)に公開したい → 50ユーザー超で有料
- カスタムドメイン(
lab.example.com等)で運用したい → ネームサーバーを Cloudflare に移管する作業が必要
2. アーキテクチャ全体像
データフロー
- 訪問者は固定URL
https://main.{project}.pages.dev/にアクセス - Cloudflare Access がメールアドレス入力を要求
- 許可リストに入っているアドレスにのみ6桁コードがメール送信される
- コード入力で認証成功 → 24時間有効な認証Cookieが発行される
- Pages の静的コンテンツが配信される
- コンテンツ更新は
git pushで自動的に Pages に反映される(1〜2分)
3. 前提条件
必要なアカウント
| サービス | 用途 | 費用 |
|---|---|---|
| Cloudflare | Pages + 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ステップ)
Cloudflare アカウント作成 + 2FA 有効化
- https://dash.cloudflare.com/sign-up でアカウント作成
- 確認メールのリンクで本人確認
- 右上アイコン → My Profile → Authentication タブ → Two-Factor Authentication を Enable
- Authenticator アプリで QR コードを読み取り、6桁コードで確認
- リカバリーコードを必ず保存(パスワードマネージャ推奨)
Zero Trust 有効化(チーム名設定)
- Cloudflare ダッシュボード左メニューから Zero Trust
- 初回はチーム名(team domain)入力を要求される → 例
office840 - プラン選択画面で Free を選択(50ユーザーまで永久無料)
- 支払い情報を登録(Free プランでは課金されないが登録は必須)
{team}.cloudflareaccess.com となる。確認: 設定 → 「チーム名とドメイン」で値を控える。
GitHub プライベートリポジトリ作成
- GitHub にログイン → + → New repository
- Repository name:
{project名}(例:office840-lab) - Visibility: Private 必須
- Add a README file はチェックを外しても OK(空リポでも進められる)
- Create repository
ローカルにクローン + サンプルサイト作成 + 初回 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.html と style.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
warning: in the working copy of '...', LF will be replaced by CRLF という警告は無視して OK。Cloudflare Pages プロジェクト作成(GitHub 連携)
- Cloudflare ダッシュボード → コンピュート → Workers & Pages
- 右上 アプリケーションを作成する → 画面下部の「Pages を導入しようとお考えですか? 始める」をクリック
- 既存の Git リポジトリをインポートする → 始める
- GitHub に接続 → Only select repositories で
{project}のみ許可 → Install & Authorize - リポジトリ
{project}を選択 → セットアップの開始 - ビルド設定: フレームワーク なし, ビルドコマンド 空欄, 出力ディレクトリ 空欄(静的HTMLの場合)
- 保存してデプロイする → 1〜2分で完了
完了すると https://{project}.pages.dev/ でサンプルサイトが 誰でも閲覧可能な状態 で公開される。次のステップで保護をかける。
Pages の「Access ポリシー」を有効化(最重要)
このステップが 本ノウハウ集の最大のポイント。Access Application を手動で作るより、Pages 側から自動連携する方が確実。
- Cloudflare → Workers & Pages →
{project}をクリック - 上部タブの 設定 → 左サブメニュー 一般
- 「Access ポリシー」項目の 有効にする をクリック
これだけで以下が 自動的に 行われる:
- Access Application が新規作成される(名前:
{project} - Cloudflare Pages) - 宛先が
*.{project}.pages.devに自動設定される(プレビュー含むワイルドカード) - 初期ポリシー
Allow Members - Cloudflare Pagesが作成され、現在ログイン中のメールアドレスが許可リストに追加される
pages.dev が出てこず詰まる。Pages 側からの自動連携なら正しい宛先で生成される。*.{project}.pages.dev 全体(プレビュー + 一部の固定エイリアス)が保護対象。本番URL {project}.pages.dev (apex) だけは別途追加が必要(後述)。許可メールアドレスを Access ポリシーに登録
初期状態では現在ログインしているメールしか許可されていない。他メンバーを追加する場合のみこの手順を行う。
- Cloudflare → Zero Trust → Access コントロール → アプリケーション
{project} - Cloudflare Pagesをクリック- ポリシー タブ →
Allow Members - Cloudflare Pagesをクリック - 「ポリシールール」の Include で Selector: Emails, Value にメールアドレスを列挙
- 保存
複数のメールアドレスを許可する場合の代表例:
- 個別列挙:
alice@example.com,bob@example.com... - ドメイン全許可: Selector を Emails ending in にして値を
@example.com
認証方法(Selector)の選び方
| Selector | 意味 | 使いどころ |
|---|---|---|
Emails | メールアドレス完全一致 | 個別ユーザー指定の基本形 |
Emails ending in | メールドメインで一括 | 同じ会社のメンバー全員 |
認証方法 (SMS) | SMSコード認証経由のみ許可 | 携帯番号認証を強制したい時、メールルール保存で call stack エラーが出る時の代替 |
Country | アクセス元の国 | 日本国内のみ等の地域制限 |
IP ranges | IPアドレス範囲 | オフィスIPのみ等 |
動作確認
- ブラウザで シークレットウィンドウ(Ctrl+Shift+N) を開く
https://main.{project}.pages.dev/にアクセス- Cloudflare Access の認証画面が表示される
- 許可リストのメールアドレスを入力 → Send me a code
- メールに6桁コードが届く → 入力 → Sign in
- サンプルサイトが表示されたら成功
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 |
ブランチプレビュー | そのブランチ最新 | 保護対象 ✓ |
*.{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.dev は foo.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側の解決手順:
- PowerShell(管理者)で
ipconfig /flushdns - ブラウザのDNSキャッシュもクリア
- Chrome/Edge:
chrome://net-internals/#dns→ Clear host cache
- Chrome/Edge:
- それでもダメなら端末の DNS を一時的に
1.1.1.1(Cloudflare) や8.8.8.8(Google) に変更 - 最終的には DNS TTL が切れる(5〜60分)のを待てば解消
6-6. Cloudflare ダッシュボードの一時障害&認証方法スイッチによる回避
「Maximum call stack size exceeded」「エラーが発生しました。リロードしてください」といった汎用エラーが出ることがある。これは Cloudflare 側の一時障害(ダッシュボード上部に黄色バナーで表示されることが多い)。
回避策(優先順):
- 認証方法を切り替える(実証済み): ポリシー作成時、Selector に
Emailsを選んで保存しようとして call stack エラーが発生する場合、Selector を認証方法 (Authentication Method) = SMSやCountry等に変えると保存できるケースがある。これは Cloudflare ダッシュボードの一時的な内部バグ回避策。 - 30分〜数時間待ってから再試行(障害復旧待ち)
- cloudflarestatus.com で障害状況確認
6-7. 認証ゲートの動作確認は必ずシークレットウィンドウで
通常ウィンドウだと既存の認証Cookieで素通りしてしまい、本当に保護されているか分からない。
回避策: Ctrl+Shift+N でシークレットウィンドウを開いて確認。
6-8. ポリシー編集が壊れた場合の「現状維持」判断
Web UI で「メール許可リスト編集」が異常終了する状態が続くと、つい代替ポリシーを作って迂回したくなるが、これは セキュリティホールを生む典型パターン。冷静に対処すること。
原則(優先順):
- 既存ポリシーが動作しているなら、削除せずそのまま使う。動いている設定を壊さないことが最優先(ロックアウト防止)。
- 暫定で別認証方法のポリシーを作る回避策は推奨しない。「認証方法 = SMS」「認証方法 = MFA」のような Selector 単独 Allow ポリシーは、結果的に「MFA を完了した誰でも通れる」抜け穴になりやすい。
- メンバー追加・変更が不要なら現状維持で問題なし。個人利用や少人数運用なら、初期作成時の許可リストのまま長期運用できる。
メンバー追加が必要になった時の試行順:
- 別ブラウザで試す(Chrome→Edge / Firefox など)。キャッシュ・拡張機能の影響を排除
- シークレットウィンドウで Cloudflare ダッシュボードにログインし直して編集
- ブラウザのCookie・キャッシュ完全クリア → 再ログイン
- 別PCで試す
- 数時間〜1日待つ(Cloudflare ダッシュボードの一時的なロールアウト不具合の可能性)
- Cloudflare API + cURL で
policiesエンドポイント直接操作 - Cloudflare サポートに問い合わせ
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. クイックチェックリスト
次回別サイトを作るときに、このリストだけ見れば再現可能。
| # | 項目 | 確認 |
|---|---|---|
| 1 | Cloudflare アカウント作成 + 2FA + リカバリーコード保存 | ☐ |
| 2 | Zero Trust 有効化 + チーム名決定 | ☐ |
| 3 | GitHub プライベートリポジトリ作成 | ☐ |
| 4 | ローカルにクローン → サンプル index.html 作成 → push | ☐ |
| 5 | Cloudflare Pages プロジェクト作成 → GitHub 連携 → デプロイ確認 | ☐ |
| 6 | Pages 「設定 → 一般 → Access ポリシー → 有効にする」 | ☐ |
| 7 | 許可メールアドレス追加(複数メンバー時)。エラー出る場合は Selector=認証方法(SMS) で代替ポリシー作成 | ☐ |
| 8 | シークレットウィンドウで main.{project}.pages.dev 動作確認 | ☐ |
| 9 | 共有用URLを main.{project}.pages.dev でメンバー周知 | ☐ |
| 10 | (任意)apex URL の追加保護 Application 作成 | ☐ |
| 11 | 許可メールアカウントの 2FA・リカバリーコード保管(ロックアウト対策) | ☐ |