SiteOps 導入ガイド
SiteOpsは、既存の会社サイトに記事管理、問い合わせフォーム、問い合わせ対応を追加するツールです。本体は自社のCloudflareアカウント上のWorker・D1・R2で動作し、既存サイトの表示、URL、ホスティングは変更せずに使えます。
導入後にできること
1. 事前準備
導入を始める前に、次のものを用意します。
node -v でバージョンを確認します。siteops-0.8.0.tgz)。vendor/siteops.tgz の名前で配置し、リリースに記載されたSHA-512値と照合して、改ざんされていないことを確かめてから使います。手元に揃えておく情報
ディレクトリ構成
SiteOps専用フォルダは任意の場所に作成できます。既存サイトのフォルダとは完全に独立しており、位置関係の制約は「既存サイトのプロジェクト内には作らないこと」の1つです。配布ファイルは次の配置を前提にコマンドを記載します。
work/ ← 任意の作業フォルダ(場所は自由) ├─ vendor/ │ └─ siteops.tgz ← 受け取った配布ファイルをこの名前で配置 └─ company-siteops/ ← これから作成するSiteOps専用フォルダ
2. セットアップ
SiteOps専用プロジェクトを作成します。実行場所は配布ファイル(vendor/)を置いた作業フォルダで、既存サイトのフォルダとは無関係です。
配布ファイルの検証
リリースに記載されたSHA-512値と、次のコマンドの出力を照合します。
shasum -a 512 vendor/siteops.tgz # Windows (PowerShell) — リリースノートの値を貼り付けると True/False で判定できます: (Get-FileHash vendor\siteops.tgz -Algorithm SHA512).Hash -eq "<リリースノートのSHA-512>"
セットアップの実行
mkdir company-siteops cd company-siteops npm init -y npm install ../vendor/siteops.tgz npx siteops setup .
会社名管理画面に表示する名称
管理者メール初期管理者の連絡先
# 対話できない環境では2項目を引数で渡します npx siteops setup . --yes --name "株式会社サンプル" --admin-email admin@example.com
セットアップ完了時に so_admin_ で始まる管理者トークンが1回だけ表示されます。管理画面へのログインに使うため、パスワードマネージャーへ保存してください。控えは .siteops/credentials.json(Git対象外)にあり、紛失時は npx siteops auth reset で再発行できます。
SiteOps専用プロジェクトに作成されるもの
| ファイル | 役割 | Gitでの扱い |
|---|---|---|
siteops.config.json | 会社名、SiteOps ID、各アダプター設定。公開サイトURLは接続時に追加 | コミット |
package.jsonとlockfile | siteops依存と、siteops:deploy など siteops:* 運用コマンド(npm scripts)を追加 | コミット |
.gitignore | .siteops/をGit対象から除外 | コミット |
.siteops/ | 管理者トークン、Worker Secret、デプロイ状態、ローカル接続先 | Git対象外 |
3. デプロイ
SiteOps本体(Worker、D1、R2、管理画面)を会社のCloudflareアカウントへ配置します。実行場所はSiteOps専用フォルダ(company-siteops/)です。
npm run siteops:deploy は未ログインの場合にブラウザのログイン画面(wrangler login)を自動で開きます。SiteOpsを置くアカウントを選んで承認してください。CI・自動化などブラウザを開けない環境では、Cloudflareダッシュボードで作成したAPIトークンを環境変数 CLOUDFLARE_API_TOKEN に設定します。認証エラー時にwranglerが提案する --temporary は一時アカウント向けのため、会社アカウントへの配置では選ばず、上記いずれかの認証を用意してください。デプロイ前には確認プロンプトが表示されるため、対話できない環境での実行では npm run siteops:deploy -- --yes のように --yes を付けます。
# 実行場所: company-siteops/ npm run siteops:deploy
成功すると SiteOps deployed、管理画面URL(Administration URL)、サイトデータの初期化確認が表示されます。初期化まで完了すると公開APIをすぐに利用できます。表示されたURL(SiteOps URLの /login)を開き、保存した管理者トークンでログインできることを確認します。
途中で止まった場合は npm run siteops:status が現在の進捗と次に実行するコマンドを表示します。
4. 既存サイトへの接続
SiteOpsと既存サイトの間に共有コードはなく、公開APIで接続します。接続情報の取得、公開URLの登録、既存サイト側の実装、の順に進めます。
- 接続情報を取得
npx siteops website manifest --jsonがSiteOps URL、SiteOps ID(APIパスの {siteKey})、記事・画像・フォームAPIのURLを返します。ブラウザへ渡るのは公開情報に限られます。 - 公開URLと反映方式を登録
npx siteops connect live --originで登録し、もう一度npm run siteops:deployで反映します。方式は3つ: リクエスト時に表示するSSR・動的サイトはlive、ビルド時にHTMLを生成する静的サイトはwebhook、承認工程を挟む運用はmanual(反映方式の詳細)。ビルドフックを用意できない検証環境や移行中はmanualで開始し、後からconnect webhookの再実行と再デプロイで切り替えられます。検証環境ではhttp://127.0.0.1:8788のようなURLも登録できます。成功すると反映方式と許可オリジン(Allowed origins)が表示されます。 - 既存サイトへ実装下記の公開APIを既存サイトのデータ取得処理とフォームへ組み込みます。実装を終えたら既存サイトを普段の手順でデプロイし、「5. 動作確認」へ進みます。
# 実行場所: company-siteops/ npx siteops website manifest --json # 手順1 npx siteops connect live --origin "https://www.example.com" # 手順2 npx siteops doctor # 設定の点検(任意) npm run siteops:deploy # 手順2の反映 # 手順3: 下記の実装 → 既存サイトを普段の手順でデプロイ npm run siteops:verify
https://www.example.com は実際の公開URLへ置き換えます。静的サイトの反映方式は記事の反映方式、メールを使う場合はメール接続を参照してください。
公開APIエンドポイント
website manifest --json が実際のURLへ展開した一覧を返します。管理トークンを使わず、公開済みデータとフォーム受付に接続できます。
GET /api/public/sites/{siteKey}/content/{contentType}/entries — 公開済みの記事を {entries} で返します。GET /api/public/sites/{siteKey}/content/{contentType}/entries/{slug} — 1件を {entry} で返します。下書きは公開APIへ出ません。GET /api/public/sites/{siteKey}/assets/{key} — 公開画像を返します。imgのsrcへそのまま使えます。GET /api/public/sites/{siteKey}/forms/security — Turnstileの公開キーとactionを返します。POST /api/public/sites/{siteKey}/forms/{formKey}/submissions — 検証と保存を行い {accepted: true} を返します。セットアップ直後から使える既定リソース
コンテンツタイプとフォームは、次のものが最初から用意されています。管理画面で項目の追加・変更ができます。
| 種類 | キー | 内容 |
|---|---|---|
| コンテンツタイプ | news | お知らせ。項目: title(必須) / displayDate / category / excerpt / body / externalUrl / important / seoTitle / seoDescription |
| コンテンツタイプ | columns / interviews / pages | コラム / インタビュー / 固定ページ |
| フォーム | contact | お問い合わせ。受付項目: name(必須) / email(必須) / company / message(必須) |
| フォーム | partner | 代理店・協力会社からの問い合わせ。受付項目: company(必須) / name(必須) / email(必須) / phone / businessType / message(必須) |
記事エントリの構造
一覧は {entries: [...]} 、詳細は {entry: {...}} を返します。既定の編集画面では本文(body)はプレーンテキスト(段落は空行区切り)で保存されるため、HTMLへの変換はサイト側で行います。
{
"entries": [
{
"slug": "sakura-no-ie-open-house",
"publishedAt": "2026-07-17T03:25:00.000Z",
"updatedAt": "2026-07-17T03:25:00.000Z",
"data": {
"title": "新モデルハウス完成見学会",
"displayDate": "2026-07-20",
"category": "イベント",
"excerpt": "一覧や検索結果に見せる概要",
"body": "本文はプレーンテキストで返ります。\n\n段落は空行で区切られます。",
"seoTitle": "SEOタイトル(任意)",
"seoDescription": "SEO説明(任意)"
}
}
]
}
記事一覧と記事詳細の実装
サーバー側(SSR、ビルド処理)で公開APIを取得し、title、meta description、canonical、構造化データを既存サイトのHTMLへ出力します。"news"はコンテンツタイプ(記事の種類を表すID)で、管理画面の記事設定と一致させます。
const SITEOPS_URL = "https://siteops-xxxxx.workers.dev"; // manifestのSITEOPS_URL
const SITE_KEY = "main"; // manifestのSITEOPS_SITE_KEY
const api = SITEOPS_URL + "/api/public/sites/" + SITE_KEY;
const list = await fetch(api + "/content/news/entries");
const { entries } = await list.json();
const detail = await fetch(api + "/content/news/entries/" + slug);
if (detail.status === 404) {
// 既存サイトの404処理へ接続
}
const { entry } = await detail.json();
問い合わせフォームの実装
フォームはブラウザから公開APIへ直接送信します。許可オリジンの検証、レート制限、サイズ上限、Turnstileの検証はSiteOps側で実行されます。contactは管理画面で作成したフォームID、JSONの各キーは受付項目のキーと一致させます。Turnstileのウィジェット作成と秘密キーの登録は、フォーム保護の設定を参照してください。
const response = await fetch(api + "/forms/contact/submissions", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
name,
email,
message,
turnstileToken // フォーム保護がTurnstileの場合。未設定(none)なら省略できます
})
});
const result = await response.json();
if (!response.ok || result.accepted !== true) {
throw new Error(result.error || "inquiry_submission_failed");
}
- フォーム保護設定を読む
GET .../forms/securityからTurnstileの公開キーとactionを取得し、ウィジェットへ渡します。 - 公開APIへ直接送信するSiteOpsが許可オリジン、サイズ、Turnstileを検証して問い合わせ一覧へ保存します。
- 保存結果で完了表示を出す
accepted: trueを受付完了として扱います。メール通知の結果はレスポンスのdeliveryフィールドへ分かれて返ります。
許可オリジンの検証は、ブラウザからの送信(Originヘッダー付き)に適用されます。Originヘッダーを送らないサーバー間リクエストはこの検証の対象外で、ボット対策の実体はTurnstileです。
5. 動作確認
公開前に、SiteOps本体、既存サイト、フォーム、メール(設定した場合)を確認します。
npx siteops doctor --remote npm run siteops:verify npx siteops backup create npx siteops backup verify backups/<作成されたディレクトリ>
backup createが保存先(既定はbackups/日時)を表示し、その値をbackup verifyへ渡します。
チェックリストの確認コマンド例
# 下書きが公開APIへ出ないこと(公開前のslugは404)
curl -i {SITEOPS_URL}/api/public/sites/{siteKey}/content/news/entries/下書きのslug
# 許可していないオリジンからのブラウザ送信が拒否されること(403)
curl -i -H "Origin: https://evil.example.com" {SITEOPS_URL}/api/public/sites/{siteKey}/forms/security
siteops verify 成功時の表示
PASS runtime SiteOps本体へ接続できました (312 ms)。 PASS website 公開APIが https://www.example.com からの接続を許可しています(フォーム保護: turnstile)。ページ表示とフォームは既存サイト側で確認します。
FAILが1件でもあると終了コード1で停止し、原因の場所と次の対処を表示します。
SiteOpsの更新
配布ファイル運用では、新しいリリースのtgzでvendor/siteops.tgzを置き換え、npm install ../vendor/siteops.tgzで入れ替えます。その後npx siteops doctor --remoteとnpx siteops deploy --dry-runを通してからデプロイします。npm run siteops:updateはnpmレジストリ配布を利用する場合の更新手段です。
復旧
管理者トークンを紛失した場合はnpx siteops auth resetで再発行します。D1とR2のバックアップはハッシュ検証と復元リハーサル(backup verify)を通過した状態で保管します。
メール接続
基本セットアップでフォームの問い合わせを管理画面へ保存できます。社内通知、受付メール、管理画面からの返信を使う段階で送信サービスを接続します。
メール接続の方式を確認送信サービス、DNS、会社メール取り込みの構成別手順
複数行コマンドの「\」はbashの行継続記号です。PowerShellでは1行へ連結して実行します。
--forms-only、SendGrid、SESでは現在の会社メール受信先を維持します。Cloudflareで会社メールも取り込む構成では、受信MXがCloudflareへ切り替わります。実行前にCLIが変更内容を明示します。
問い合わせ保存から開始現在の会社メールを維持
問い合わせ一覧への保存は基本セットアップで有効です。このレシピは送信停止の状態を設定として明示します。送信接続を追加すると社内通知と受付メールも有効になります。
npx siteops mail setup disabled npx siteops deploy
Cloudflare DNSで通知を送信会社メールのMXは現在のまま
ブラウザでCloudflare認証を行い、Email Sending、Workerデプロイ、検証を自動実行します。認証情報はCloudflare CLIが管理します。
npx siteops cloudflare connect \ --from-email no-reply@example.com \ --from-name "Example Company" \ --reply-to contact@example.com \ --notification-emails team@example.com \ --forms-only
Cloudflare DNSで送信+会社メール受信Email Routing有効化でMXが変わる
指定した会社アドレスをSiteOps Workerへ接続します。実行前にCLIがMX変更を明示し、既存ルールと競合する場合は停止します。
npx siteops cloudflare connect \ --from-email no-reply@example.com \ --from-name "Example Company" \ --reply-to contact@example.com \ --notification-emails team@example.com \ --addresses contact@example.com,support@example.com
Cloudflare以外のDNS+SendGrid現在のDNSへCNAMEを追加
送信専用APIキーをSiteOpsへ登録します。デプロイ後に管理画面で認証レコードを発行し、Xserver、さくら、Route 53など現在のDNSへ追加して「DNSの反映を確認」を押します。
npx siteops mail setup sendgrid \ --from-email no-reply@example.com \ --from-name "Example Company" \ --reply-to contact@example.com npx siteops secret set SITEOPS_SENDGRID_API_KEY npx siteops deploy
Cloudflare以外のDNS+Amazon SES現在のDNSへDKIMを追加
SiteOps専用のIAMの認証情報を登録し、権限をSESの送信APIに限定します。
npx siteops mail setup ses \ --from-email no-reply@example.com \ --from-name "Example Company" \ --reply-to contact@example.com \ --region ap-northeast-1 npx siteops secret set SITEOPS_AWS_ACCESS_KEY_ID npx siteops secret set SITEOPS_AWS_SECRET_ACCESS_KEY npx siteops deploy
Agentic Inbox外部メールボックスを送受信に使う
会社所有のメールボックスを送受信アドレスに指定し、Cloudflare AccessのService Authで保護します。
npx siteops mail setup agentic-inbox \ --from-email support@example.com \ --from-name "Example Company" \ --base-url https://inbox.example.com \ --mailbox support@example.com npx siteops secret set SITEOPS_AGENTIC_INBOX_ACCESS_CLIENT_ID npx siteops secret set SITEOPS_AGENTIC_INBOX_ACCESS_CLIENT_SECRET npx siteops deploy
トラブルシューティング
導入はどの段階からでも再開できます。
npm run siteops:status が4段階の進捗と、次に実行する1件のコマンドを表示します。中断後の再開もここから始めます。site_not_found 等が出る場合は npx siteops doctor --remote が初期化と診断を実行します。その後に再試行してください。npm run siteops:deploy がD1・R2の消失を検知して再作成します(データは空の状態へ戻ります)。データの復元は backup verify を通過したバックアップから行います。CLOUDFLARE_API_TOKEN、確認プロンプトは --yes、秘密値は --secret-env <環境変数名> または npx siteops secret set <NAME> --from-env <環境変数名> で渡します。基本フロー(セットアップ〜動作確認)の対話プロンプトにはすべて非対話の代替があります。例外はCloudflareメール接続(siteops cloudflare connect)で、ブラウザでのOAuth承認が必要なため対話できる端末で一度実行します。npx siteops doctor --remote が設定、認証情報、Cloudflare側の状態をPASS/WARN/FAILで点検し、原因の場所を特定します。npx siteops connect live --origin で新しい公開URLを登録し、npm run siteops:deploy で反映します。npm run siteops:deploy での反映、既存サイトが公開中であることを順に確認します。npx siteops auth reset が新しいトークンを発行し、既存のブラウザセッションを無効化します。npx siteops backup verify <バックアップディレクトリ> でハッシュ検証と復元リハーサルを通してから復元します。付録
構成別の詳細
構成の考え方、公開APIリファレンス、反映方式、フォーム保護の技術手順です。
付録
構成別の詳細
構成の考え方、公開APIリファレンス、反映方式、フォーム保護の技術手順です。
構成の考え方
既存サイト、SiteOps、メールを分けて接続
公開サイトがXserver、DNSがCloudflareという構成にも対応します。ホスティング、DNS、メールはそれぞれ現在の契約に合わせて選べます。
既存サイトとの接続方式
サイトを移転せずに接続する方法
既存サイトはVercel、Xserver、さくら、AWS、Cloudflareなど現在のホスティングのまま、公開HTTPS APIでSiteOpsと接続します。フレームワークを問わず接続方法は同一で、既存サイトへのコード生成やパッケージ追加はありません。
公開APIリファレンス
website manifest --jsonは次のURLを実際のSiteOps URLとSiteOps IDへ展開して返します。管理トークンを使わず、公開済みデータとフォーム受付へ接続できます。
GET /api/public/sites/{siteKey}/content/{contentType}/entries
GET /api/public/sites/{siteKey}/content/{contentType}/entries/{slug}
GET /api/public/sites/{siteKey}/assets/{key}
GET /api/public/sites/{siteKey}/forms/security
POST /api/public/sites/{siteKey}/forms/{formKey}/submissions
SEOと反映方式
記事の反映方式
公開APIは公開済みの記事を返します。静的サイトではビルド時に完全なHTMLを生成できるため、通常の静的CMSと同じSEO構成にできます。複数行コマンドの「\」はbashの行継続記号です。PowerShellでは1行へ連結して実行します。
APIから表示
SSR、サーバー、エッジでリクエスト時に公開記事を取得し、title、canonical、JSON-LDをサーバー側で生成します。
npx siteops connect live \ --origin https://www.example.com
静的サイトを再ビルド
記事公開後に非公開ビルドURLを呼び出します。ビルド失敗時は前回成功したサイトを維持し、記事一覧は成功したビルドから配信します。
npx siteops connect webhook \ --origin https://www.example.com \ --webhook-url-env SITE_BUILD_HOOK
手動反映
移行中や別の承認工程を通す場合に使います。SiteOpsへ公開記事を保存し、承認後に既存サイトへ反映する運用です。
npx siteops connect manual --origin https://www.example.com
問い合わせフォーム
フォーム保護の設定
SiteOpsはフォーム項目、通知条件、自動返信を管理します。公開サイトは見た目と入力UIを持ち、Turnstileで確認したトークンをSiteOpsへ渡します。
- Turnstileウィジェットを作成Cloudflareダッシュボードの「Turnstile」でウィジェットを作成し、公開する正確なホスト名(例: www.example.com。検証環境なら localhost や 127.0.0.1)を登録します。作成するとサイトキー(公開)とシークレットキー(秘密)の2つが発行されます。サイト自体がCloudflare以外のホスティングでも利用できます。
- 2つのキーをSiteOpsへ接続サイトキーはコマンドの引数で渡し、シークレットキーは非表示プロンプトへ入力するか、非対話環境では
--secret-envで環境変数から渡します。ローカルの認証情報とWorker Secretへ保存されます。 - ウィジェットをフォームへ組み込む下記の組み込み例のとおり、
/forms/securityから取得したサイトキーとactionでウィジェットを描画し、応答トークンをturnstileTokenとして送信JSONへ含めます。 - テスト問い合わせを確認保存成功とメール送信結果を分けて確認します。メール失敗時も問い合わせデータは保存済みの状態を維持します。
# 対話環境(シークレットキーは非表示プロンプトへ入力) npx siteops forms protect turnstile \ --turnstile-site-key "0x4AAAA..." # 非対話環境(CI・自動化) TURNSTILE_SECRET="シークレットキー" npx siteops forms protect turnstile \ --turnstile-site-key "0x4AAAA..." --secret-env TURNSTILE_SECRET npx siteops deploy
複数行コマンドの「\」はbashの行継続記号です。PowerShellでは1行へ連結して実行します。方式・サイトキーの変更は siteops deploy で反映され、シークレットキーの差し替え(npx siteops secret set SITEOPS_PUBLIC_FORM_TURNSTILE_SECRET_KEY --from-env <環境変数名>)は配備済みの環境へ即時反映されます。
ウィジェットの組み込み例
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
<div id="turnstile-widget"></div>
// フォーム保護設定を取得してウィジェットを描画する
const { protection } = await (await fetch(api + "/forms/security")).json();
if (protection.mode === "turnstile") {
turnstile.render("#turnstile-widget", {
sitekey: protection.turnstileSiteKey,
action: protection.action
});
}
// 送信時: turnstile.getResponse() の値を turnstileToken としてJSONへ含めます
サーバー側の検証とエラー
SiteOpsは送信ごとに (1)トークンの存在 (2)Cloudflareでのトークン検証 (3)actionが siteops-form と一致 (4)送信元Originのホスト名がウィジェット登録ホスト名と一致、の4段階を検証します。
| エラーキー | 意味 | 対処 |
|---|---|---|
public_form_turnstile_token_required | トークン未送信 | ウィジェットの応答値を turnstileToken で送ります |
public_form_turnstile_verification_failed | トークン検証に失敗 | サイトキーとシークレットキーが同じウィジェットの組か確認します |
public_form_turnstile_action_invalid | actionが不一致 | ウィジェット描画時のactionを siteops-form に合わせます |
public_form_turnstile_hostname_invalid | 送信元ホスト名が不一致 | 公開ホスト名をウィジェットへ登録します(下記のテストキーの注意も参照) |
Cloudflare公式のテストキー(常に成功: サイトキー 1x00000000000000000000AA + シークレット 1x0000000000000000000000000000000AA、拒否確認用シークレット 2x0000000000000000000000000000000AA)で流れを確認できます。ただしテストキーの検証応答はホスト名が example.com 固定のため、ローカル環境からのブラウザ送信は必ず public_form_turnstile_hostname_invalid になります。ブラウザまで含めた受理確認は、実ウィジェットに公開ホスト名を登録して行います(Originヘッダーを送らないサーバー間送信ではホスト名検証の対象外)。