# ホスティングと配信

> どのプランでも使えるホスティング（siteon deploy・rollback・サブドメイン・反映時間・停止。Free プラン は「Made with Siteon」バッジつき）と、いまのホスティングに置いたまま使う方法（Cloudflare Pages の中継・data-embed）。

URL: https://siteon.dev/ja/docs/hosting

Siteon は、AI で作ったサイトのためのインフラです。AI や手で作った静的サイト（HTML）に、ホスティング・お客さん（お店の人）が直せる CMS・お問い合わせフォーム・アクセス解析・AI エージェントからの操作（CLI・MCP）を足し、そのまま納品できる状態にします。

## 3つの置き方

CMS の値を HTML に入れるやり方は3つあります。フォームと流入元の記録は、どれでも script だけで動きます。

| | Siteon のホスティング | Cloudflare Pages の中継 | `data-embed`（ほかのホスティング） |
|---|---|---|---|
| 置き場所 | Siteon（`siteon deploy` で公開） | お客様の Cloudflare Pages | お客様のホスティング |
| 値が入るとき | 配信の前（サーバー側） | 配信の前（サーバー側） | 表示されたあと（ブラウザ側） |
| 検索エンジン・JavaScript 無効の人 | 値の入ったページが届く | 値の入ったページが届く | 静的な文言が拾われることがある |
| 表示のチラつき | 無い | 無い | 一瞬、静的な文言が見える |
| 記事ごとのページ・タグページ・記事の `url` | 使える | 使える | 使えない |
| 管理画面の GTM ID の差し込み | 使える | 使える | 使えない（`data-gtm` を使う） |
| 直した内容の反映 | 15 秒以内 | 最長 1 分 | 最長 1 分 |

## ホスティング（siteon deploy）

ビルドしたフォルダ（`dist/` など）を、コマンド1つで公開します。Free プラン でも公開でき、有料プラン（Lite・Standard・Business）では容量が増え、独自ドメイン （近日）とバッジの非表示が使えます。追加の料金はかかりません。

- 公開先は `https://<サブドメイン>.siteun.com/` です
- HTML は配信の前に CMS の値を埋め込みます。中継のファイルも `data-embed` も要りません。script タグはフォームと流入元の記録のためにそのまま入れます
- 操作は [CLI と MCP サーバー](https://siteon.dev/ja/docs/cli-mcp) から行います。API キーには `deploy:write` の権限が要ります
- Free プランのサイトも `https://<サブドメイン>.siteun.com/` で公開でき、検索エンジンにも載ります。ページの隅に小さな「Made with Siteon」のバッジが付きます（有料プランでは付きません）。Free の公開は1日20回までで、公開のたびに自動で検査します（疑わしい版は公開せずに保留します）
- 独自ドメインとバッジの非表示は、有料プラン（月500円から）で使えます

### はじめての公開

```sh
siteon hosting enable --site hanaya     # 初回だけ。サブドメインを決める
siteon deploy ./dist --site hanaya      # 公開。URL が表示されます
```

- サブドメインは、何も指定しなければサイト ID になります
- サブドメインに使えるのは、英小文字・数字・ハイフンの 3〜63 文字です（先頭と末尾にハイフンは不可）。`www`・`admin`・`mail` などの予約語や、銀行・決済・大手のブランド名を含む名前は使えません
- サイト ID が条件に合わないときは `subdomain_required` で止まります。`--subdomain 別の名前` を付けて実行し直します
- 一度使われていたサブドメインは、解放から 90 日間、ほかのアカウントでは使えません（リンク先の乗っ取りを防ぐため）
- サブドメインの変更は 1 サイトにつき月 3 回までです。管理画面の「ホスティング」→「サブドメイン」からも変えられます
- `--site` を省くと、`sitekit/site.json` の `id` を使います

### deploy で起きること

1. フォルダの中のファイルを読み、ファイルごとの sha256 を計算します
2. サーバーにまだ無いファイルだけを送ります（6 本並列）。前回から変わっていないファイルは送りません
3. 全部届いたら、公開中の版を一度に切り替えます。途中で失敗しても、公開中のサイトは前の版のままです

通信が切れたときは CLI が同じ操作として送り直すので、二重に公開されることはありません。失敗したときは、途中まで送った分の予約を取り消してから終わります。

### 置けるファイル

| 項目 | 決まり |
|---|---|
| 拡張子 | `html` `htm` `css` `js` `mjs` `json` `txt` `xml` `svg` `png` `jpg` `jpeg` `webp` `avif` `gif` `ico` `woff` `woff2` `ttf` `mp4` `webm` `pdf` `webmanifest` `map` だけ。ほか（`exe` `zip` `apk` など）は `file_type_not_allowed` で送る前に止まる |
| 予約パス | `admin/`・`v1/`・`_sk/` で始まるパスは使えない（`reserved_path`。大文字・小文字は区別しない） |
| ドットファイル | `.git`・`.env`・`.DS_Store` など、ドットで始まる名前は送らない。`.well-known/` だけは公開する |
| 1 ファイル | 25MiB まで（`file_too_large`） |
| 容量・ファイル数・保持する版 | プランごと（下の表） |

| | Free | Lite | Standard | Business |
|---|---|---|---|---|
| 容量（保持している版の合計） | 20MB | 200MB | 1GB | 5GB |
| 1 回の公開のファイル数 | 200 | 1,000 | 5,000 | 20,000 |
| 保持する版（公開中を含む） | 2 | 5 | 20 | 50 |

容量は、保持している版が使っているファイルの合計です。同じ中身のファイルは、版をまたいで1つとして数えます。写真（管理画面からアップロードしたもの）の容量とは別です。

### URL とファイルの対応

| リクエスト | 返すもの |
|---|---|
| `/` | `index.html` |
| `/about` | `about.html`（拡張子の無いファイル `about` があればそれ） |
| `/about/` | `about/index.html` |
| `/about.html`・`/index.html`・`/about/index.html` | 拡張子や `index.html` の無い形（`/about`・`/`・`/about/`）へ 301 で移す（クエリは保つ） |
| `/about/`（`about/index.html` が無く `about.html` がある） | `/about` へ 301 |
| `/about`（`about/index.html` がある） | `/about/` へ 301 |
| `/sitemap.xml`・`/robots.txt` | サイトに置いてあればそれ。無ければ自動で作って返す（下記） |
| 見つからない | サイトに `404.html` があればそれを 404 で返す。無ければ共通の 404 ページ |

1 つのページに URL が 1 つだけになるよう、揺れは 301 でそろえます。サイト内のリンクに `.html` が付いていてもページには着きますが、リンクは拡張子の無い形で書くのがおすすめです。

**sitemap.xml と robots.txt の自動生成:** サイトに置いていなければ、公開中の HTML（`404.html`・`_` で始まるテンプレート・noindex のページを除く）と記事ごとのページから sitemap.xml を作ります。robots.txt はすべてを許可し、sitemap.xml の場所を書いたものを返します。自分で置いたファイルは上書きしません。

- 記事ごとのページ・タグページ・多言語のパスは、[中継](#cloudflare-pages-の中継)と同じ書き方で動きます（[記事ごとのページ](https://siteon.dev/ja/docs/cms#記事ごとのページ)）
- 言語の接頭辞は schema.json の `locales` から決まります。先頭の言語が `/`、ほかは `/<言語コード>` です（例: `["ja", "en"]` なら `ja:/,en:/en`）。記事ページのパスは `news` です。どちらも変える手段は準備中です （近日）

### 反映までの時間とキャッシュ

公開の切り替え・`rollback`・管理画面での保存・停止は、**15 秒以内**に全ページへ反映されます。ブラウザに残っているファイルは、次の期間まで古いまま見えることがあります。

| ファイル | ブラウザのキャッシュ |
|---|---|
| HTML | 毎回確かめる（`no-cache`） |
| ファイル名にハッシュが入ったもの（`app.3f9a8b7c.js` など）、`_astro/`・`assets/` の下 | 1 年（`immutable`） |
| 画像・フォント・動画・PDF | 1 日 |
| それ以外（CSS・JS・JSON など） | 5 分 |

ビルドツールの「ファイル名にハッシュを付ける」設定を使うと、公開した瞬間から新しいファイルが届きます。保存した直後の見え方は、管理画面のプレビューで確かめます。

### 前の版に戻す（rollback）

```sh
siteon deploys --site hanaya              # 版の一覧（* が公開中）
siteon rollback --site hanaya             # 1 つ前の版に戻す
siteon rollback <deploy_id> --site hanaya # 指定した版に戻す
```

戻せるのは保持している版だけです（保持する版の数は上の表）。古い版は、新しい公開に押し出されると消えます（`deploy_not_retained`）。

### 検索エンジンとの関係

| サイトの状態 | 検索エンジン | 画面 |
|---|---|---|
| 有料プラン | 載る | そのまま |
| Free プラン、または解約した・未払いが 31 日を過ぎて Free と同じ扱いになったサイト | 載る | ページの隅に小さな「Made with Siteon」のバッジが付く |
| 独自ドメインを使っているときの共通サブドメイン （近日）| 載らない（独自ドメインへの canonical） | そのまま |

### 表示が止まるとき・止まらないとき

**支払いが遅れても、解約しても、上限を超えても、配信は止めません。** 止まるのは次のときだけです。

| 状態 | 応答 |
|---|---|
| 悪用（フィッシングなど）の通報や検査で停止したサイト | 全ページが `451` の停止ページ |
| サイトを削除した・アカウントを閉じた | `404` |
| まだ一度も公開していない | `404`「まだ公開されていません」 |
| Siteon 側の障害でサイトの状態を確かめられない | `503`（数秒後にもう一度） |

- 停止中でも `deploy` は受け付けます（直してから申し立てるため）。公開は停止が解けてから反映されます
- 未払いの 8〜30 日目は、新しい `deploy` ができません（`402`）。解約・未払い 31 日目以降は Free と同じ扱いになり、Free の上限の範囲で公開できます。どの場合も公開中の版はそのまま配信します（支払いの状態ごとの動きは [プランと上限](https://siteon.dev/ja/docs/plans#支払いが止まったとき)）

### 独自ドメイン （近日）

お店のドメイン（`www.example.jp`）で公開できます（受け付けは準備中です）。

- `www` を本体にし、`www.example.jp` を `CNAME` で Siteon に向けます。所有の確認のため `TXT` レコードも追加します
- `example.jp`（www の無い形）は `www` への転送にします
- 使える数は Lite 1・Standard 3・Business 10 です（`www` と www の無い形の組で 1 つ）
- 独自ドメインが有効になると、共通サブドメインは検索に載らない設定になり、独自ドメインを正規の URL として示します

DNS に入れるレコード・DNS の会社ごとの入れ方・状態とエラーの意味は [独自ドメインとフォームの許可ドメイン](https://siteon.dev/ja/docs/domains#custom-domain) にあります。

### 公開前の検査と通報

`deploy` のたびに、HTML を自動で検査します。パスワードの入力欄・ほかのサイトへ送信するフォーム・ブランド名とログインを促す語の組み合わせが見つかると、Free プランでは公開せず `422` `check_failed` で止めます（公開中の版はそのまま配信します）。Free プランは、さらに公開の前に URL の検査を通します。有料プランでは公開したうえで、検査に回します。

悪用（フィッシングなど）されているサイトは、`https://siteun.com/report` の通報フォームから知らせられます。

## Cloudflare Pages の中継

### 置くファイル

サイトの `functions/` に中継のファイルを2つ置きます。

```text
functions/_middleware.ts
functions/_cms/render.ts
```

- `_cms/render.ts` は生成物です。編集しません（編集すると、更新のときに食い違います）
- 外部向けの配布の手段（ダウンロード・CLI）は準備中です （近日）

### 環境変数

Pages プロジェクトの `vars`（`wrangler.jsonc` か Cloudflare のダッシュボード）で設定します。

| 変数 | 必須 | 説明 |
|---|---|---|
| `CMS_SITE` | 必須 | サイト ID |
| `CMS_ORIGIN` | 必須 | `https://cdn.siteon.dev`（末尾のスラッシュは不要） |
| `CMS_LOCALES` | | `言語:接頭辞` のカンマ区切り。省略すると `ja:/` |
| `CMS_NEWS_PATH` | | 記事ページのパス兼コレクションのキー。省略すると `news` |
| `CMS_INDEXABLE_HOSTS` | | 検索に載せてよいホスト名のカンマ区切り。省略すると何もしない |

`CMS_SITE` か `CMS_ORIGIN` が無いと、中継は何もせず静的な HTML をそのまま返します。

```jsonc
{
  "name": "hanaya",
  "compatibility_date": "2026-08-01",
  "pages_build_output_dir": "public",
  "vars": {
    "CMS_SITE": "hanaya",
    "CMS_ORIGIN": "https://cdn.siteon.dev",
    "CMS_LOCALES": "ja:/,en:/en",
    "CMS_NEWS_PATH": "news"
  }
}
```

### 言語の判定

`CMS_LOCALES` は `ja:/,en:/en,zh-tw:/zh-tw` のように書きます。先頭が主言語です。パスの**最長一致**で言語が決まります。

| パス | 言語 |
|---|---|
| `/`, `/about/` | `ja`（知らないパスもここに落ちる） |
| `/en`, `/en/`, `/en/about`, `/en.html` | `en` |
| `/english` | `ja`（接頭辞で始まるだけの別ページは一致しない） |
| `/zh-tw/news/x` | `zh-tw` |

`Accept-Language` は見ません。同じ URL は誰が見ても同じ言語です。

### 検索避け（`CMS_INDEXABLE_HOSTS`）

プレビュー用のホスト（`*.pages.dev` など）と本番のホストで同じ中身を配ると、重複したページとして扱われ、プレビューのほうが検索結果に出ることがあります。`CMS_INDEXABLE_HOSTS` に**本番のホストだけ**を書くと、それ以外のホストで返す HTML に `X-Robots-Tag: noindex` が付きます。

```jsonc
"CMS_INDEXABLE_HOSTS": "www.example.jp,example.jp"
```

| 値 | 意味 |
|---|---|
| 未設定・空 | 何もしない |
| `www.example.jp,example.jp` | この2つ以外のホストの HTML に `noindex` |
| `-` | どのホストも検索に載せない（本番のドメインがまだ無いとき） |

- 完全一致です（`www.example.jp` を書いても `a.www.example.jp` は許可されない）。大文字・小文字は区別しません
- 付くのは HTML だけです（画像や CSS には付けない）
- サイト側が `_headers` などで `X-Robots-Tag` を出している場合は上書きしません

### 反映までの時間

公開 JSON は 60 秒キャッシュされ、中継が返す HTML も `max-age=60` です。お店の人が「公開」を押してから**最長1分**で全ページに反映されます。サイトの再デプロイは要りません。

### CMS が止まっているとき

**静的な HTML をそのまま返します。** 取得の失敗・タイムアウト（5秒）・壊れた JSON のどれでも、埋め込みをしなかったページを返すだけで、サイトは止まりません。

例外は記事ページだけです。取得に成功したうえで ID が見つからないときは 404 を返します。取得できなかったときは雛形をそのまま返します。

### 動いているかの確かめ方

中継が値を埋め込むと、レスポンスヘッダに `x-sitekit-rendered: 1` が付きます。**GET で確かめます。`curl -I`（HEAD）では付きません。**

```bash
curl -sD - -o /dev/null https://example.com/ | grep -i sitekit
curl -s https://example.com/ | grep -o 'data-sk-rendered="1"'
```

付いていなければ、環境変数の設定漏れか、CMS に届いていないか、そのページが HTML（200）ではありません。

## ほかのホスティング（`data-embed`）

Cloudflare Pages 以外（関数を動かせない静的ホスティングなど）では、script に `data-embed` を付けます。

```html
<script src="https://cdn.siteon.dev/sitekit.js" data-site="hanaya" data-embed defer></script>
```

- 表示のあとに公開 JSON を取得して、`data-cms` 系の印に値を入れます
- **`document.body` の中身を丸ごと置き換えます。** body の要素に付けたイベントは消えます（フォームは自動で付け直します）。サイト独自の JavaScript がある場合は避けてください
- 取得に失敗したときは何もしません。静的な HTML が残ります
- 記事ごとのページ（`/news/<id>`）・タグページ・記事の `url` は中継の機能なので使えません

> 中継を使うサイトでは **`data-embed` を付けません。** 埋め込みは2回実行できません（1回目の値を2回目が上書きします）。中継済みのページには `data-sk-rendered="1"` が付いていて script 側は何もしませんが、そもそも併用しないでください。`data-embed` の無い script は中継と併用します（フォームと流入元の記録に要ります）。
