# 🧠 設定模型

Pingclair 在載入 Pingclairfile 時編譯設定，預先完成位址解析與匹配器編譯等工作，供後續請求使用。不支援或無效的設定會在載入時被拒絕。本頁說明 **v0.2.2** 的設定載入與重載方式。

## 🗂️ 檔案由全域選項與網站區塊組成

```caddyfile
{
    email admin@example.com
}

example.com {
    encode zstd gzip
    reverse_proxy 10.0.0.10:8080 10.0.0.11:8080
}

:8080 {
    file_server ./public
}
```

- **全域選項** 寫在檔案最上方一個沒有名稱的區塊裡。它們設定不屬於任何單一網站的事：ACME 帳號的 email、Admin API、自動 HTTPS、受信任的代理，以及主機名稱上游的 DNS 重新解析。[指令參考](/zh-TW/reference/directives/#global-options)列出了所有全域選項。
- **網站區塊** 以位址命名：主機、連接埠，或兩者皆有。連接埠是位址的一部分，而不是另一個指令，所以位址與監聽器不可能互相矛盾。
- **指令** 是網站區塊內的敘述。有些接受參數，有些接受巢狀區塊，有些兩者都接受。
- **註解** 以 `#` 開頭，直到該行結尾。
- **含有空白的值要加引號。** 時間長度必須帶單位：`30s` 是三十秒，在需要時間長度的地方寫一個單獨的 `30` 會被拒絕。

## 🧭 匹配器選出指令適用的請求

具名匹配器以 `@name` 宣告，使用時把名稱寫在指令後面：

```caddyfile
example.com {
    @api path /api/*
    header @api Cache-Control "no-store"

    @assets path /assets/*
    header @assets Cache-Control "public, max-age=31536000, immutable"
}
```

`handle` 區塊把同一條路由的指令放在一起。一個請求只會由一個 `handle` 區塊回應，而沒有匹配器的 `handle` 會接住其他區塊沒接到的所有請求：

```caddyfile
example.com {
    handle /assets/* {
        file_server ./assets
    }

    handle {
        respond "Page Not Found" 404
    }
}
```

## 🚦 哪一條路由回應請求

路由先依指令種類排序，第一條匹配的路由負責回應。`redir`、`handle` 與 `route` 排在 `respond` 之前，`respond` 又排在 `reverse_proxy`、`php_fastcgi` 與 `file_server` 之前。相同指令的單一路徑先比較移除尾端 `*` 後的長度，再將精確路徑排在同名萬用路徑之前，其餘保留檔案順序。多路徑或無路徑的匹配器排在單一路徑之後。

使用互斥的 `handle` 區塊區分路由，或用 `route` 保留書寫順序。路徑比較忽略 ASCII 大小寫，並解碼一次百分比編碼；需要區分大小寫時使用 `path_regexp`。`handle`、`handle_path` 與 `route` 在區塊前只接受 `*`、以 `/` 開頭的路徑或 `@name`，不接受 `*.php` 這類裸字串。

[CHANGELOG](https://github.com/dorianverlaine/pingclair/blob/main/CHANGELOG.md) · [Pingclairfile](/zh-TW/reference/pingclairfile/)

## 🧩 以片段與匯入重複使用設定

片段（snippet）是以 `(name) { ... }` 宣告、以 `import name` 插入的可重用設定。呼叫端可以傳入參數與一個區塊；片段在寫著 `{block}` 的地方接收那個區塊：

```caddyfile
(site) {
    https://{args[0]} {
        {block}
    }
}

import site example.com {
    reverse_proxy 127.0.0.1:3000
}
```

在被匯入的檔案中定義的片段，後續的匯入都看得到。指令參數清單內的佔位符會被拒絕：Caddy 在插入片段後會重新讀取那一行，而 Pingclair 的 parser 做不到，因此會拒絕這種寫法並顯示錯誤。

## 🛡️ 驗證會拒絕伺服器做不到的事

`pingclair validate` 會編譯檔案，並執行語法以外的檢查：指令參數、匹配器語法、憑證與金鑰檔案是否存在，以及政策限制，例如哪些對端可以設定用戶端身分相關的標頭。

沒通過這些檢查的設定不會執行。判斷失敗與否的規則有三條：

- **未實作的名稱會被指名拒絕。** Pingclair 認得 Caddyfile 格式定義的每一個名稱。對於沒有實作的名稱，它會回報該功能不存在；絕不會把它當成拼錯的字，也絕不會默默忽略。
- **不支援的選項會被拒絕。** `encode br` 是編譯錯誤，因為沒有串流式的 Brotli 編碼器；不會自動改用 gzip。
- **語法正確但指向不存在的檔案，仍然是錯誤。** 伺服器儲存庫中的 `examples/full_featured.pingclair` 語法正確，但在它所指的憑證路徑不存在的機器上，`validate` 仍會拒絕它。

伺服器載入檔案時也會執行相同的檢查，重載時也一樣。

## 🔁 重載在不重啟的情況下替換設定

重載會重新讀取檔案、編譯，並在行程持續執行的同時把結果換上去。如果新檔案編譯失敗，先前的設定會繼續提供服務。要求重載有三種方式：

- `pc service reload`（或 `systemctl reload pingclair`）透過已安裝的 unit 送出 `SIGUSR1`。
- `sudo kill -USR1 "$(systemctl show -p MainPID --value pingclair)"` 直接送出同一個訊號。
- `pingclair reload` 透過 Admin API 進行，並印出伺服器的判定結果。它需要 `admin` 全域選項。

`systemctl reload` 只能回報訊號已送達，所以伺服器的判定結果會出現在 unit 的狀態列與 journal 中。

有些變更無法透過重載套用；這時伺服器會拒絕重載並保留舊設定，而不是只套用其中一部分：

- **監聽器的變更。** 新增、移除或搬移位址，或讓監聽器在明文與 TLS 之間切換，都需要重啟，因為監聽 socket 是在啟動時建立的。Unix 上從空設定啟動可第一次載入明文 HTTP；TLS 與後續拓撲變更仍需重啟。
- **全域選項。** 啟動時就確定的選項（例如 `trusted_proxies`）作用於整個行程，修改這類政策需要重啟；行程日誌設定可重載。
- **憑證拓撲。** 新增 TLS 主機名稱，或改變網站取得憑證的方式，都需要重啟。

拒絕訊息會指出是哪一項變更，例如 `listener topology changed (added:
…, removed: …)`；執行 `sudo pc service restart` 即可套用。

[以服務方式執行](/zh-TW/start/service/#-重載意味著什麼)說明各種重載結果。

## 🧭 相關頁面

- [Pingclairfile](/zh-TW/reference/pingclairfile/)：完整的語言說明。
- [指令參考](/zh-TW/reference/directives/)：所有指令與選項。
- [架構](/zh-TW/concepts/architecture/)：執行編譯後設定的是什麼。
