在開發 API 時,如果希望允許特定網域,如 wingzero.tw
及其所有子網域(如 api.wingzero.tw
、admin.wingzero.tw
)存取 API,就需要設定 CORS(跨來源資源共享,Cross-Origin Resource Sharing)。然而,瀏覽器的 CORS 機制不允許 Access-Control-Allow-Origin
使用萬用字 *
來匹配部分子網域,因此我們需要手動處理。
1. 問題點
直接使用 header("Access-Control-Allow-Origin: *.wingzero.tw");
這種寫法是不被瀏覽器接受的,因為 Access-Control-Allow-Origin
不能包含萬用字 *
來匹配特定網域。因此,我們需要手動檢查請求的 Origin
,並在回應時動態設定 Access-Control-Allow-Origin
。
2. 解決方案:動態設定 CORS
我們可以在 CI_Controller
的 __construct
方法中處理 CORS 設定,確保允許 wingzero.tw
及其所有子網域。
程式碼範例
defined('BASEPATH') or exit('No direct script access allowed'); class Email extends CI_Controller { public function __construct() { parent::__construct(); // 允許所有 *.wingzero.tw 的網域 if (isset($_SERVER['HTTP_ORIGIN'])) { $origin = $_SERVER['HTTP_ORIGIN']; if (preg_match('/^https?:\/\/([a-zA-Z0-9-]+\.)?wingzero\.tw$/', $origin)) { header("Access-Control-Allow-Origin: $origin"); } } // 允許必要的 CORS 設定 header("Access-Control-Allow-Methods: GET, POST, OPTIONS"); header("Access-Control-Allow-Headers: Content-Type, Authorization"); header("Access-Control-Allow-Credentials: true"); } public function send() { // 處理 OPTIONS 預檢請求 if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') { exit; } $data = [ "stats" => "ok" ]; // return json $this->output ->set_content_type('application/json') ->set_output(json_encode($data)); } }
3. 程式碼解析
(1) 動態設定 Access-Control-Allow-Origin
-
$_SERVER['HTTP_ORIGIN']
會取得發送 API 請求的來源網址(如https://api.wingzero.tw
)。 -
使用
preg_match('/^https?:\/\/([a-zA-Z0-9-]+\.)?wingzero\.tw$/', $origin)
來檢查請求是否來自wingzero.tw
或其子網域。 -
如果符合,則將
Access-Control-Allow-Origin
設為該Origin
,確保瀏覽器允許請求。
(2) 處理 OPTIONS 預檢請求(Preflight Request)
當 API 接受 POST
或有特殊標頭(如 Authorization
)時,瀏覽器會先發送 OPTIONS
預檢請求來確認伺服器是否允許該請求。
-
若請求方法為
OPTIONS
,則直接exit;
,避免影響後續請求。
(3) 允許的 HTTP 方法與標頭
-
Access-Control-Allow-Methods: GET, POST, OPTIONS
:允許GET
、POST
,並允許OPTIONS
預檢請求。 -
Access-Control-Allow-Headers: Content-Type, Authorization
:允許Content-Type
和Authorization
等標頭,確保 API 可以處理 JSON 及身份驗證。 -
Access-Control-Allow-Credentials: true
:允許跨域請求攜帶憑證(如cookies
或Authorization
標頭)。
透過這種方式,我們能夠讓特定網域及其所有子網域存取 API,並且符合瀏覽器的 CORS 安全規則。這種方法可以確保 API 安全性,同時避免 CORS 相關錯誤,提高跨域請求的兼容性!