
HTTP CORS 完全攻略:跨域資源共享問題分析與解決方案
更新日期:2025 年 5 月 18 日
在現代網頁開發中,幾乎每位前端開發者都曾遭遇過這個令人頭痛的錯誤訊息:「Access to fetch at 'https://api.example.com' from origin 'https://example.com' has been blocked by CORS policy」。這個看似神秘的 CORS 錯誤,已成為 Web 開發過程中最常見的障礙之一。但究竟什麼是 CORS?為什麼會發生這些錯誤?又該如何正確解決?本文將深入淺出地剖析 CORS 機制的前因後果,並提供全面的解決方案,幫助開發者徹底掌握跨域資源共享的技術挑戰。
什麼是 CORS?
CORS(Cross-Origin Resource Sharing,跨域資源共享)是一種 HTTP 標頭機制,允許 Web 應用程式在不同網域間安全地共享資源。它擴展了同源政策(Same-Origin Policy)的限制,使得網頁能夠請求不同來源的資源,同時仍保持必要的安全性。
簡單來說,CORS 是瀏覽器的一種安全機制,用來控制一個網站能否載入另一個網域的資源。這是一個前端安全的重要屏障,能夠有效防止惡意網站竊取用戶數據或進行跨站請求偽造攻擊。
同源政策:CORS 的基礎
要理解 CORS,我們必須先了解「同源政策」這個概念。在 Web 安全模型中,瀏覽器會限制網頁發起的跨源 HTTP 請求。所謂「同源」,是指協議、網域名稱和埠號都相同。
以 https://example.com/page.html
為例,下列情況都被視為「非同源」:
- 不同協議:
http://example.com/page.html
(https vs http) - 不同網域:
https://api.example.com/data.json
(example.com vs api.example.com) - 不同埠號:
https://example.com:8080/page.html
(默認 443 vs 8080)
同源政策預設會阻止網頁透過 JavaScript 取得上述「非同源」資源,而 CORS 正是為了在保持安全性的前提下,允許這類跨域請求的機制。

為什麼會發生 CORS 問題?
CORS 問題之所以如此普遍,主要是由於現代 Web 應用的架構特點與瀏覽器安全機制之間的衝突。讓我們深入探討這些原因:
現代 Web 架構的變化
- 前後端分離:現代 Web 開發通常採用前後端分離架構,前端 UI 和後端 API 往往部署在不同的網域。
- 微服務架構:一個應用可能調用多個不同網域的微服務 API。
- 第三方服務整合:許多應用需要整合第三方 API(如支付、社交媒體、地圖服務等)。
- 靜態資源 CDN:靜態資源通常部署在 CDN 上,與主應用不在同一網域。
瀏覽器安全模型的演進
同源政策是瀏覽器安全的基石,但隨著 Web 應用越來越複雜,這一政策需要適當放寬。CORS 正是為解決這一矛盾而誕生的標準機制。當 CORS 問題發生時,實際上是因為:
- 瀏覽器執行了保護:瀏覽器檢測到跨域請求,但服務器未正確配置 CORS 頭部。
- 防止未授權訪問:這一機制旨在防止惡意網站在用戶不知情的情況下訪問敏感信息。
- 服務端未明確允許跨域:服務器需要明確告訴瀏覽器哪些跨域請求是被允許的。
值得注意的是,CORS 錯誤是純粹的瀏覽器限制。如果使用 Postman 或服務器端代碼發送相同請求,是不會受到 CORS 限制的,因為這些工具不執行瀏覽器的同源政策。這也是為什麼有時候 API 看起來工作正常,但在瀏覽器中卻出現 CORS 錯誤。

CORS 的工作流程
CORS 機制的核心在於 HTTP 頭部的交換。當瀏覽器發起跨域請求時:
- 請求階段:瀏覽器在 HTTP 請求中添加
Origin
頭部,標明請求的來源。 - 回應階段:服務器檢查
Origin
頭部,如果允許該來源的跨域請求,則在響應中添加Access-Control-Allow-Origin
頭部。 - 驗證階段:瀏覽器檢查響應中的 CORS 頭部,如果
Access-Control-Allow-Origin
匹配當前來源(或為萬用字元*
),則允許網頁訪問響應內容。
預檢請求(Preflight Request)
對於某些「非簡單請求」(如使用 PUT/DELETE 方法,或包含自定義頭部的請求),瀏覽器會先發送「預檢請求」:
- 預檢請求使用
OPTIONS
方法,詢問服務器是否允許實際請求。 - 服務器必須通過返回適當的 CORS 頭部來回應這一詢問。
- 只有預檢請求成功,瀏覽器才會發送實際請求。
技術筆記:許多 CORS 問題出現在預檢請求階段,因為後端常常忽略了對 OPTIONS 請求的正確處理。
常見的 CORS 錯誤類型
CORS 錯誤可能以多種形式出現,了解常見錯誤類型有助於我們更快地診斷和解決問題:
缺少 Access-Control-Allow-Origin 頭部
Access to fetch at 'https://api.example.com/data' from origin 'https://example.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
這是最常見的 CORS 錯誤,表示服務器沒有返回必要的 Access-Control-Allow-Origin
頭部,或該頭部的值不包含請求的來源。
憑證相關錯誤
Access to fetch at 'https://api.example.com/data' from origin 'https://example.com' has been blocked by CORS policy: The value of the 'Access-Control-Allow-Credentials' header in the response is '' which must be 'true' when the request's credentials mode is 'include'.
當前端請求包含憑證(如 cookies)但服務器未正確配置 Access-Control-Allow-Credentials
頭部時,會出現此錯誤。
不允許的方法或頭部
Access to fetch at 'https://api.example.com/data' from origin 'https://example.com' has been blocked by CORS policy: Method PUT is not allowed by Access-Control-Allow-Methods in preflight response.
預檢請求失敗,因為服務器未明確允許請求中使用的 HTTP 方法或頭部字段。
萬用字元與憑證衝突
The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'.
當請求包含憑證(credentials: 'include'
)時,服務器不能使用萬用字元 *
作為 Access-Control-Allow-Origin
的值,必須明確指定允許的來源。
如何解決 CORS 問題
解決 CORS 問題通常需要從後端入手,但也有一些前端或架構層面的解決方案。以下是全面的解決方案指南:
後端解決方案
最直接且最推薦的方法是在服務器端正確配置 CORS 頭部:
Node.js/Express 解決方案
使用 cors
中間件:
const express = require('express');
const cors = require('cors');
const app = express();
// 基本用法(允許所有來源)
app.use(cors());
// 或自定義配置
app.use(cors({
origin: 'https://example.com', // 允許的來源
methods: ['GET', 'POST', 'PUT', 'DELETE'], // 允許的 HTTP 方法
allowedHeaders: ['Content-Type', 'Authorization'], // 允許的請求頭
credentials: true // 允許攜帶憑證(cookies)
}));
// 特定路由單獨配置
app.get('/api/public-data', cors(), (req, res) => {
// 處理請求
});
Python/Django 解決方案
使用 django-cors-headers
套件:
# settings.py
INSTALLED_APPS = [
# ...其他應用
'corsheaders',
]
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware',
# 務必放在其他中間件之前
# ...其他中間件
]
# 允許所有來源
CORS_ALLOW_ALL_ORIGINS = True
# 或指定允許的來源
CORS_ALLOWED_ORIGINS = [
"https://example.com",
"https://sub.example.com",
]
# 允許憑證
CORS_ALLOW_CREDENTIALS = True
PHP 解決方案
在 PHP 腳本中添加必要的頭部:
<?php
// 允許特定來源
header("Access-Control-Allow-Origin: https://example.com");
// 或允許所有來源(不建議用於生產環境)
// header("Access-Control-Allow-Origin: *");
// 允許的方法
header("Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS");
// 允許的頭部
header("Access-Control-Allow-Headers: Content-Type, Authorization");
// 允許憑證
header("Access-Control-Allow-Credentials: true");
// 預檢請求快取時間(秒)
header("Access-Control-Max-Age: 86400");
// 處理 OPTIONS 預檢請求
if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
exit(0);
}
// 實際請求的處理邏輯
// ...
?>
Nginx 代理配置
通過 Nginx 添加 CORS 頭部:
server {
# ...其他配置
location /api/ {
# 添加 CORS 頭部
add_header 'Access-Control-Allow-Origin' 'https://example.com';
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization';
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Max-Age' '86400';
# 處理 OPTIONS 預檢請求
if ($request_method = 'OPTIONS') {
add_header 'Content-Type' 'text/plain charset=UTF-8';
add_header 'Content-Length' 0;
return 204;
}
# 反向代理到實際服務
proxy_pass http://backend-service;
# ...其他代理配置
}
}
前端解決方案
雖然 CORS 問題最好在後端解決,但有時候我們無法修改後端代碼(如使用第三方 API)。這時可考慮以下前端解決方案:
根據瀏覽器的同源政策(Same-Origin Policy),前端網頁若嘗試從不同源請求資源,瀏覽器會自動啟動 CORS 機制,要求後端明確允許該來源才能讀取資料。這是一種瀏覽器主動限制「回應存取」的安全防護措施。實際上,請求仍然會被發送出去,伺服器也可能已收到並處理,只是若回應中缺少正確的 CORS 標頭(如 Access-Control-Allow-Origin),瀏覽器便會阻止 JavaScript 存取回應資料。
因此,當我們無法控制第三方伺服器時,只能透過「變更請求來源或繞過瀏覽器限制」的方式來解決。例如使用中介代理伺服器來轉發請求,使得對瀏覽器而言,請求來源仍然是同源的,或是在開發環境中設定本地代理以模擬同源情境。這些做法的核心原理都是:避免讓瀏覽器觸發跨域的安全限制。
使用代理伺服器
開發環境中,可利用框架內建的代理功能:
- Vite:在
vite.config.js
中配置代理export default { server: { proxy: { '/api': { target: 'https://api.example.com', changeOrigin: true, rewrite: (path) => path.replace(/^/api/, '') } } } }
- Create React App:在
package.json
中配置代理{ "proxy": "https://api.example.com" }
生產環境代理解決方案
生產環境中,可以設置自己的代理服務器:
- 架設代理服務器:使用 Nginx 或 Node.js 建立代理,將前端請求轉發到目標 API 並添加適當的 CORS 頭部。
- 雲端代理服務:使用 Cloudflare Workers、AWS Lambda 或其他無伺服器解決方案建立輕量級代理。
- API 閘道:如果使用雲端架構,可配置 API Gateway 處理 CORS。
注意:代理方案解決的是前端跨域問題,但代理伺服器本身仍需處理與目標 API 的授權認證問題。切勿將敏感憑證暴露在前端代碼中。
JSONP 方案(傳統方案)
JSONP (JSON with Padding) 是一種繞過同源政策的傳統技術,利用 <script>
標籤不受同源限制的特性:
// 前端實現
function jsonpRequest(url, callback) {
const script = document.createElement('script');
const callbackName = 'jsonp_' + Math.random().toString(36).substring(2, 15);
// 全局回調函數
window[callbackName] = function(data) {
callback(data);
document.body.removeChild(script);
delete window[callbackName];
};
// 添加回調參數到 URL
script.src = url + (url.includes('?') ? '&' : '?') + 'callback=' + callbackName;
document.body.appendChild(script);
}
// 使用示例
jsonpRequest('https://api.example.com/data', function(data) {
console.log('Received data:', data);
});
注意:JSONP 只支援 GET 請求,且有安全隱憂,現代應用中已經很少使用。
架構層面的解決方案
除了直接處理 CORS 問題,有時候我們可以通過調整應用架構來避免跨域問題:
- 合併部署:將前端應用與 API 部署在同一域名下,避免跨域請求。
- 子域策略:將 API 部署在主域的子域下(如 api.example.com),並通過適當設置 Cookie 的 domain 來共享會話。
- BFF 模式:採用 Backend for Frontend 模式,為前端提供專用的 API 層,統一處理跨域問題。
- 統一網域名稱系統:使用 Nginx 等反向代理,將不同服務統一到同一域名下的不同路徑。
CORS 錯誤是什麼時候發生的?
許多開發者會疑惑:「當前端對不同源的 API 發送請求時,是在請求發出前就被攔截,還是在收到回應時才報錯?」
答案是:請求其實已經發送出去了,但錯誤發生在瀏覽器解析回應時。
瀏覽器基於安全考量,會對跨來源請求啟用 CORS 機制。當後端未回傳正確的 CORS 標頭(如 Access-Control-Allow-Origin
),即使請求成功送達,瀏覽器也會阻止 JavaScript 存取回應資料,並在開發者工具中報錯。
這種錯誤不是網路傳輸失敗,而是瀏覽器主動封鎖對回應內容的存取。
所以簡單來說:請求發得出去,問題發生在回應階段。
最佳實踐與安全考量
在解決 CORS 問題時,應該遵循以下最佳實踐,確保既滿足功能需求,又不犧牲安全性:
CORS 配置最佳實踐
- 避免使用萬用字元:生產環境中避免使用
Access-Control-Allow-Origin: *
,應明確指定允許的來源域名。 - 僅允許必要的 HTTP 方法:在
Access-Control-Allow-Methods
中只列出應用實際需要的方法。 - 設置合理的預檢快取時間:通過
Access-Control-Max-Age
設置適當的預檢請求快取時間,減少 OPTIONS 請求。 - 謹慎處理憑證:只在必要時設置
Access-Control-Allow-Credentials: true
,且確保正確配置Access-Control-Allow-Origin
。 - 動態設置來源頭部:根據請求的
Origin
頭部動態設置Access-Control-Allow-Origin
,而不是硬編碼。
安全考量
CORS 是一個安全機制,過於寬鬆的配置可能帶來風險:
- CSRF 風險:允許憑證跨域可能增加跨站請求偽造 (CSRF) 攻擊的風險,應確保實施其他防護措施。
- 數據洩露:過於寬鬆的 CORS 配置可能允許惡意站點讀取敏感響應數據。
- 子域安全:允許所有子域(如
*.example.com
)可能存在風險,若其中某個子域被攻擊者控制。 - 預檢繞過:某些簡單請求不觸發預檢,應確保服務器端也實施適當的身份驗證和授權。
最佳實踐提示:在生產環境中,建議根據實際需要設置嚴格的 CORS 策略,只允許必要的來源、方法和頭部,並定期審查 CORS 配置。
CORS 問題診斷工具
遇到 CORS 問題時,以下工具可幫助快速診斷:
- 瀏覽器開發者工具:在網絡面板(Network tab)中可查看請求頭部、響應頭部和詳細的 CORS 錯誤。
- CORS 檢測工具:線上工具如 CORS Test 可幫助測試 API 端點的 CORS 配置。
- 後端日誌:檢查服務器日誌中 OPTIONS 請求的處理情況。
- curl 工具:使用 curl 發送帶有 Origin 頭部的請求,檢查服務器響應中的 CORS 頭部。
總結:CORS 不再是難題
CORS 錯誤雖然常見又令人困擾,但理解其背後的原理和解決方案後,處理起來就變得清晰明了。記住以下關鍵點:
- CORS 是瀏覽器的安全機制,保護用戶免受潛在的跨站攻擊。
- 最佳解決方案是在後端正確配置 CORS 頭部,特別是
Access-Control-Allow-Origin
。 - 當無法修改後端時,代理服務器是一個實用的替代方案。
- 安全與便利性之間需要平衡,應避免過於寬鬆的 CORS 配置。
- 現代 Web 框架和雲服務通常提供內建的 CORS 支持,簡化了配置過程。
通過掌握本文介紹的知識和技術,你應該能夠自信地應對開發中遇到的各種 CORS 挑戰,構建出真正無縫整合的現代 Web 應用。
如有其他關於 Web 開發或前後端整合的問題,歡迎繼續關注我們的技術文章系列!