> For the complete documentation index, see [llms.txt](https://docs.ipcheck.ing/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.ipcheck.ing/developer/zh-tw/reference/faq.md).

# 常見問題

最常見部署與設定問題的解答。

以下每個答案都描述程式碼中的實際行為，不是猜測。如果這裡某些內容與你的實例不符，你很可能使用的是不同版本。

## 部署

<details>

<summary>每次 API 呼叫都回傳 403「拒絕存取」或「你在做什麼？」</summary>

全域 referer 閘門拒絕了請求。有兩種不同的訊息：

* `{"error":"你在做什麼？"}` — 該請求攜帶了 **沒有** `Referer` 標頭。
* `{"error":"拒絕存取"}` — 一個 `Referer` 已送出，但其主機名稱不被允許。

允許的主機名稱為 `localhost` 以及其中的所有內容 `ALLOWED_DOMAINS`。將其設為使用者實際載入網站所用的主機名稱：

{% code title=".env" %}

```bash
ALLOWED_DOMAINS="myip.example.com,www.myip.example.com"
```

{% endcode %}

然後重新啟動後端。有三件事常讓人踩坑：

1. **比對是精確的。** `example.com` 不包含 `sub.example.com`。請逐一列出每個主機名稱。
2. **直接以 IP 存取也算是一個主機名稱。** 瀏覽到 `http://192.168.1.10:18966` 會將該 IP 作為 referer 主機名稱送出——請將它加入，或改用主機名稱。
3. **連接埠與協定無關緊要**，只會比較主機名稱。

請參閱 [安全性選項](/developer/zh-tw/configuration/security-options.md).

</details>

<details>

<summary>重啟容器後，我的 VITE_* 變數沒有作用</summary>

`VITE_*` Vite 會在 `pnpm run build` 以及 **被內嵌到 JavaScript bundle 中**。它們不會在執行時讀取。重新啟動伺服器無法改變已經編譯進 `dist/`.

官方的 `jason5ng32/myip:latest` 映像檔是在沒有你的值的情況下建置的—— `.env` 位於 `.dockerignore`，因此也沒有任何 `.env` 在那次建置期間存在。傳入 `-e VITE_CURL_IPV4_DOMAIN=...` 給預先建置的映像檔對前端沒有任何作用。若要在 Docker 中使用建置時變數，你必須自行建置映像檔。在 Node 部署中，請重新執行 `pnpm run build`.

兩個 `VITE_*` 變數也會在執行時讀取，並且 *會* 回應 `-e`: `VITE_SENTRY_DSN_FRONTEND` （它會掛載 `/api/monitoring`） `VITE_SITE_URL` （它會建置上游 `User-Agent`）。完整說明請見 [環境變數](/developer/zh-tw/reference/environment-variables.md).

</details>

<details>

<summary>連接埠 18966 已經被使用，或者我想使用不同的連接埠</summary>

MyIP 執行兩個監聽：靜態 / SPA 伺服器在 `FRONTEND_PORT` （預設 `18966`）以及 API 伺服器在 `BACKEND_PORT` （預設 `11966`）。前端伺服器會代理轉送 `/api` 到後端，因此只需要讓前端連接埠能從外部連線。

**Node 部署** — 設定 `BACKEND_PORT` 以及 `FRONTEND_PORT` 位於 `.env` 並重新啟動。兩者都由 `backend-server.js`, `frontend-server.js` 以及 `vite.config.js`讀取，所以只改其中一個而不改另一個會導致代理失效。

**Docker** — 不要變更容器內部的連接埠；請改在主機端重新對應。該映像檔 `EXPOSE`s `18966`:

{% code title="shell" %}

```bash
docker run -d -p 8080:18966 --name myip --restart always jason5ng32/myip:latest
```

{% endcode %}

</details>

<details>

<summary>curl API 卡片從未出現</summary>

這個 `curlDomainsHadSet` 中的 getter `frontend/store.js` 會將這三個網域一起做 AND，因此只有在 **三者都** 皆非空時卡片才會顯示。只設定一個或兩個都不會顯示任何內容。請同時設定 `VITE_CURL_IPV4_DOMAIN`, `VITE_CURL_IPV6_DOMAIN` 以及 `VITE_CURL_IPV64_DOMAIN` ——並記得它們是 `VITE_*`，因此需要重新建置，而不只是重新啟動。

這些變數只提供要顯示的主機名稱。MyIP 不會提供那些端點；你需要將 DNS 紀錄指向你自己的純文字 IP 回應服務。

</details>

<details>

<summary>使用者收到 429「請求過多」</summary>

先檢查回應主體。 `429 {"message":"請求過多"}` 是 `SECURITY_RATE_LIMIT`：客戶端已超過限制，其時間窗為 **20 分鐘** ，按每個客戶端 IP 計算。帶有 `code: "quota_exceeded"` 的 429 完全是另一回事——那是由上游每帳號每月配額經由 `/api/invisibility` 或 `/api/dnsleaktest/session/:token` 轉送過來的，而且沒有任何本機設定會影響它。

{% hint style="info" %}
啟動橫幅顯示 `🛡️ 已啟用速率限制器——每 60 分鐘 N 個請求`，但在 `backend-server.js` 是 `20 * 60 * 1000` 毫秒中設定的時間窗。日誌訊息是錯的；真正的時間窗是 20 分鐘。
{% endhint %}

提高這個數值，或將其設為 `0` 0 `SECURITY_DELAY_AFTER` 是另一個獨立且較溫和的機制——它絕不拒絕，只會增加 `命中次數 × 400 毫秒` 的延遲，時間窗為 **60 分鐘** 。

單次 MyIP 頁面載入會發出許多 `/api` 請求，因此過低的限制會影響一般使用者。日誌會帶有 `IP 速率受限` 警告，並附上違規的 IP——每次進入受限狀態時只會記錄一次，而不是每個被阻擋的請求都記錄。請設定 `SECURITY_BLACKLIST_LOG_FILE_PATH` ，如果你也想要一份持久的磁碟記錄。

</details>

## MaxMind

<details>

<summary>日誌寫著「MaxMind API 在資料庫成功載入前都會回傳 503」</summary>

後端無法開啟 `common/maxmind-db/GeoLite2-City.mmdb` 以及 `GeoLite2-ASN.mmdb`。它仍然會啟動，但 `GET /api/maxmind` 會回應 503，而且 UI 中的國家徽章會一直是空白。

你通常會先看到這個警告：

{% code title="日誌" %}

```
⚠️  缺少 MaxMind 資料庫，而且未設定 MAXMIND_ACCOUNT_ID / MAXMIND_LICENSE_KEY。
  請在 .env 中設定憑證並重新啟動，或放入 GeoLite2-City.mmdb + GeoLite2-ASN.mmdb
  到 common/maxmind-db/。仍將啟動伺服器；MaxMind API 在
  資料庫可用前都會回傳 503。
```

{% endcode %}

修正方式正如訊息所說——設定憑證，或預先放入這些檔案：

{% code title=".env" %}

```bash
MAXMIND_ACCOUNT_ID="your-account-id"
MAXMIND_LICENSE_KEY="your-license-key"
MAXMIND_AUTO_UPDATE="true"
```

{% endcode %}

成功的情況如下 `📦 MaxMind 資料庫已載入（啟動）`。完整操作說明請見 [MaxMind 設定](/developer/zh-tw/getting-started/maxmind-setup.md).

</details>

<details>

<summary>全新的 Docker 容器會有一個空的 maxmind-db 目錄</summary>

這是刻意如此。GeoLite2 資料庫無法依照 MaxMind 的授權重新散布，而且 `.dockerignore` 排除了 `common/maxmind-db/*.mmdb` ，因此本機建置絕不會把 CI 建置不會有的檔案烘焙進去。

因此 Docker 部署者必須使用憑證路徑——傳入 `MAXMIND_ACCOUNT_ID`, `MAXMIND_LICENSE_KEY` 以及 `MAXMIND_AUTO_UPDATE="true"` 搭配 `-e`。沒有它們時，容器仍會啟動、提供 UI，並在 `/api/maxmind` 每次開機時對其回傳 503。

第一次下載會在開機期間進行，且上限為 5 分鐘。如果逾時，伺服器仍會持續監聽——請檢查日誌並重新啟動。請見 [使用 Docker 部署](/developer/zh-tw/getting-started/deploy-with-docker.md).

</details>

<details>

<summary>MAXMIND_AUTO_UPDATE 是「false」，但資料庫還是下載了</summary>

這是預期行為。 `MAXMIND_AUTO_UPDATE` 閘門 **只有定期排程器**。開機時的「若缺失則下載」路徑不會查詢它：如果存在有效憑證，而且 `.mmdb` 檔案不存在，就會執行一次下載循環。理由是， `.env` 中的憑證本身就已表達「我想讓 MaxMind 正常運作」的意圖。

搭配 `MAXMIND_AUTO_UPDATE="true"` 你還會在啟動 60 秒後進行首次檢查，並每 24 小時重新整理一次。 `CAIDA_AUTO_UPDATE` 對 CAIDA 資料集也以相同方式運作。

</details>

## 網路與 API

<details>

<summary>對 /api/... 使用 curl 會回傳 403，但在瀏覽器中網站正常運作</summary>

curl 不會送出 `Referer` 標頭，因此全域閘門會回應 `403 {"error":"你在做什麼？"}`。這不是錯誤——這正是這個閘門的目的。

若要手動測試端點，請提供允許的 referer：

{% code title="shell" %}

```bash
curl -H "Referer: http://localhost/" http://localhost:11966/api/configs
```

{% endcode %}

請使用列於 `ALLOWED_DOMAINS` （或 `localhost`，它永遠被允許）中的主機名稱，並直接連到後端連接埠。

</details>

<details>

<summary>UI 中缺少某些 IP 資料來源</summary>

前端會隱藏後端沒有 API 金鑰的來源。 `GET /api/configs` 每個功能只回傳一個布林值——絕不回傳金鑰值。請抓取它（使用有效的 `Referer`）以確切查看你的實例認為已設定了哪些項目： `地圖` 需要 `GOOGLE_MAP_API_KEY`, `ipapiis` 需要 `IPAPIIS_API_KEY`, `cloudFlare` 需要 `CLOUDFLARE_API_KEY`，依此類推。完整欄位列表請見 [API 端點](/developer/zh-tw/reference/api-endpoints.md)；金鑰位於 [選用 API 金鑰](/developer/zh-tw/configuration/optional-api-keys.md).

請注意，此路由會在邊緣快取 1 小時，因此新加入的金鑰可能需要這麼久才會在 CDN 後方顯示。

</details>

<details>

<summary>/api/ipapiis 或 /api/ip2location 會回傳 500</summary>

這兩個處理器是透過呼叫 `.split(',')` 在金鑰上組出上游 URL，沒有做空值檢查，因此未設定的金鑰會拋出錯誤並產生 500，而不是清楚的錯誤。請設定 `IPAPIIS_API_KEY` 或 `IP2LOCATION_API_KEY`.

在正常使用下你永遠不會看到這個： `/api/configs` 會將來源回報為不可用，而前端也不會呼叫它。

</details>

<details>

<summary>報告分享回傳 503「未設定報告分享」</summary>

`POST /api/report` 以及 `GET /api/report/:id` 需要 **三者都** Cloudflare Workers KV 變數： `CLOUDFLARE_API_KEY`, `CLOUDFLARE_ACCOUNT_ID` 以及 `CLOUDFLARE_KV_NAMESPACE_ID`。缺少其中任何一項都會讓這兩個路由回傳 503，並使 `reportSharing: false` 位於 `/api/configs`，進而完全隱藏分享 UI。

兩個常見錯誤：此權杖需要 **Workers KV 儲存空間：編輯** 權限（單純的 Radar 權杖不夠），而且 `CLOUDFLARE_KV_NAMESPACE_ID` 是該名稱空間的 **十六進位 ID** ，要用儀表板上的名稱空間 ID，而不是顯示名稱。

</details>

<details>

<summary>前端 Sentry 事件對 /api/monitoring 回傳 404</summary>

`backend-server.js` 只會在 `VITE_SENTRY_DSN_FRONTEND` 已設定時掛載隧道路由 **於伺服器程序上**。如果你在建置時把 DSN 烘焙進自建映像檔，但執行時沒有同時傳入，bundle 就會將封包送到一個從未掛載的路由。

兩個地方都要傳入相同的值。請見 [錯誤監控（Sentry）](/developer/zh-tw/configuration/error-monitoring.md).

</details>

## 開發

<details>

<summary>npm install 會破壞此專案</summary>

MyIP 是 **僅限 pnpm**。版本是透過 `packageManager` 位於 `package.json`, `pnpm-lock.yaml` 被提交，而且 `pnpm-workspace.yaml` 包含原生相依性所需的安裝腳本核准。npm 或 yarn 會產生衝突的 lockfile，並缺少那些核准。

{% code title="shell" %}

```bash
npm install -g pnpm
pnpm install && pnpm run build
```

{% endcode %}

Dockerfile 也是透過 `corepack enable`來做同樣的事，它會提供完全鎖定的 pnpm 版本。

</details>

<details>

<summary>開發伺服器無法連到後端</summary>

`pnpm dev` 會同時執行 Vite 與後端。Vite 會在 `FRONTEND_PORT` 並將 `/api` 到 `http://localhost:${BACKEND_PORT}`。如果你只改了其中一個連接埠，卻沒改另一個——或者只在 shell 中設定而沒有在 `.env` ——則代理指向的是空的。

兩者都 `vite.config.js` 以及 `backend-server.js` 會從 `.env`，所以請把它們保留在那裡。請見 [開發環境](/developer/zh-tw/development/dev-environment.md).

</details>

<details>

<summary>除了錯誤之外，沒有任何內容會被記錄</summary>

`LOG_LEVEL` 預設為 `info`，因此 `debug` 行會被抑制。除非你主動啟用，否則每個請求的 HTTP 記錄完全關閉：

{% code title=".env" %}

```bash
LOG_LEVEL="debug"
LOG_HTTP="true"
```

{% endcode %}

`LOG_HTTP=true` 每個 `/api/*` 請求都記錄一行，包含方法、URL 與狀態。它是在速率限制器之前掛載的，所以 429 也會出現。請設定 `LOG_FORMAT="json"` ，如果有日誌轉送器正在消耗輸出。請見 [記錄](/developer/zh-tw/configuration/logging.md).

</details>


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.ipcheck.ing/developer/zh-tw/reference/faq.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
