CORS Header Ownership
文件狀態:已對 new API
最後驗證:2026-05-13
來源:new code / staging curl / nginx config 排查
這份文件回答什麼
這份文件說明 API 專案中 CORS / HTTP response header 應由哪一層負責,以及排查 CORS 表象錯誤時要如何避免 PHP、Nginx、CDN 重複設定同名 header。
這份文件不回答單一 endpoint 的 business rule,也不記錄一次性 staging request payload。單一 API 行為請放在業務流程或 專案 migration docs。
核心原則
同一個 response header 必須有唯一 owner。
如果 PHP router 已經統一設定 CORS header,Nginx 不要再設定同名 CORS header。重複設定可能造成瀏覽器判定 header 無效、排查時誤判來源,或讓不同 status code 的行為不一致。
pro360_api_82 目前 owner
pro360_api_82 的 CORS owner 是 PHP router。
入口:
WebRoot/index.php
-> RouterV3::handleCORS()目前 PHP 會設定:
header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS, HEAD, PATCH');
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Headers: Content-Type, X-PRO360-Rest-Api-Key, X-Requested-With, X-PRO360-User-Session-Token');
if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
die;
}因此一般 API request / preflight 應該由 PHP 回 CORS header。
Nginx 不應該做什麼
當 PHP 已是 CORS owner 時,Nginx server block 不要再加:
add_header Access-Control-Allow-Origin "*" always;
add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS, HEAD, PATCH" always;
add_header Access-Control-Allow-Headers "Content-Type, X-PRO360-Rest-Api-Key, X-Requested-With, X-PRO360-User-Session-Token" always;這些 header 名稱已由 PHP 設定。除非明確決定把 CORS ownership 從 PHP 移到 Nginx,否則不要同時存在。
Nginx 可以做什麼
Nginx 可以加不影響瀏覽器 CORS 判斷的 debug marker,例如:
add_header x-pro360 "99" always;這類 header 只用來辨識 response 是否來自特定 origin / 特定 Nginx 節點。它不是 CORS 設定,不會修 API,也不應該被當成業務邏輯。
若排查完成且不再需要辨識 routing,可以移除 debug marker。
如果需要讓 Nginx 處理錯誤頁 CORS
有些錯誤不是 PHP 產生,例如:
413 request body too large
502 bad gateway
504 gateway timeout
Nginx 自己產生的 404 / error_page這類 response 可能不會跑到 PHP,也就不會有 PHP 設定的 CORS header。若真的需要讓 Nginx 錯誤頁也帶 CORS,必須先做 ownership decision:
- 保持 PHP 作為一般 API CORS owner。
- Nginx 只處理 Nginx 自己產生的錯誤 response。
- 不要讓同一個正常 API response 同時被 PHP 與 Nginx 加同名 CORS header。
建議做法是獨立設計 error location / error page 的 header 策略,而不是直接在整個 server block 無條件加同名 CORS header。
排查順序
遇到瀏覽器顯示 CORS error 時,不要先假設是 CORS 設定錯。先判斷 request 是否有到達 origin。
- 查瀏覽器 Network:
- Request Method 是
OPTIONS還是POST。 - 是否有 Status Code。
- Response Headers 是否為空。
- 是否顯示
Provisional headers are shown。
- Request Method 是
- 查 origin access log:
- 是否有同一時間的 request。
- status 是
200、4xx、499、502、504還是完全沒有。
- 查 origin error log:
- 是否有 PHP fatal / parse error。
- 是否有 upstream timeout。
- 用 curl 測 preflight:
curl -i -X OPTIONS 'https://api.example.com/path.json' \
-H 'Origin: https://example.com' \
-H 'Access-Control-Request-Method: POST' \
-H 'Access-Control-Request-Headers: x-pro360-rest-api-key,content-type'- 用 curl 測實際 POST,確認 response 來自哪個 origin:
serverx-pro360x-php-envx-cache
常見判讀
| 現象 | 優先判斷 |
|---|---|
OPTIONS 有 CORS header 且 200 | preflight 本身正常 |
Network 顯示 Provisional headers are shown,沒有 status | request 可能未完成送出或被 client abort |
| origin access log 沒有該 request | request 沒到這台 origin,查 CDN / ALB / frontend abort |
access log 有 499 | client / browser / frontend 中斷 request |
access log 有 502 / 504 | upstream / PHP-FPM / timeout 問題 |
| PHP parse error | PHP 未跑到 router,可能造成瀏覽器顯示 CORS 表象錯誤 |
| PHP warning | 通常不會中斷 API,但可能污染 response 或寫入 nginx error log |
修改前 checklist
要改 CORS / header 前,先回答:
- 目前 header owner 是 PHP、Nginx、ALB 還是 CDN?
- 是否已經有同名 header 在其他層設定?
- 修改是否只影響錯誤 response,還是會影響所有正常 API response?
- 是否需要
Access-Control-Allow-Credentials?如果需要,就不能搭配Access-Control-Allow-Origin: *。 - 是否有多個 origin / 舊新系統混流?要用 marker header 或 access log 確認實際來源。