專案在本機用 npm run dev 一切正常,卻在 Vite 建置後 API 404,或在 Console 看到 CORS 錯誤,是 Vite 專案上線時很常見的狀況。原因通常出在開發與生產環境的差異:server.proxy 只屬於開發伺服器,建置後的靜態檔案並不帶有這層代理。
想了解 server.proxy 各參數(changeOrigin、rewrite、bypass、ws 等)的行為,可前往 〈Vite proxy 開發伺服器轉發設定、CORS 繞過與 Production 部署排查〉 觀看。若是第一次碰到 CORS 錯誤本身,可先看 〈CORS 錯誤是什麼? 如何解決本地端跨域請求 CORS Error〉。本文聚焦在建置之後:為什麼會 404、怎麼改成正式環境的 API 網址、預檢失敗長什麼樣子,以及依序排查的步驟。
一、 開發伺服器與生產環境的差異
在 Development 環境中,瀏覽器的 API 請求送往 Vite 本地開發伺服器(例如 http://localhost:5173/api/users),再由 Vite 以伺服器對伺服器的方式轉發到 target。依 MDN 的定義,協定、連接埠與主機都相同才算同源(Same-Origin),所以瀏覽器把這個請求當成同源請求,不會進入跨來源的 CORS 檢查。
下圖把同一個頁面(來源 http://localhost:5273)的四種請求放在一起:直接打沒有 CORS 標頭的後端會被瀏覽器擋下;經過 Vite proxy 的請求正常回應;vite build 後放在不含代理的靜態主機上,同一個相對路徑回 404;vite preview 則沿用了 server.proxy。

當執行 vite build 進行 Production 建置時,產出的是純靜態 HTML、JavaScript 與 CSS 檔案,Node.js 開發伺服器並不會被打包進去。一旦部署至不含代理設定的靜態主機,客戶端的 fetch('/api/users') 便會直接對該主機發送請求,導致 404 Not Found 或無法取得後端回應。
vite preview 不等於正式主機
vite preview 的 preview.proxy 預設值就是 server.proxy,所以在本機用它檢查建置結果時,API 請求看起來一切正常,這只代表本機預覽沿用了開發時的設定,部署後的靜態主機並沒有這層代理。上線前要確認的是正式主機(Nginx、CDN 或後端)自己有對應的轉發規則,或前端改成直接呼叫有 CORS 設定的後端網址。
二、 Production 環境下的 API 請求路徑對應
由於 Vite proxy 僅在開發期生效,在 Production 環境中,通常會透過環境變數(Environment Variables)動態切換 API 基礎路徑。Vite 官方文件說明,import.meta.env.PROD 表示是否為生產環境,只有 VITE_ 前綴的變數會暴露在客戶端程式碼中,vite build 則會載入 .env.production。
// src/services/apiClient.ts
const getApiBaseUrl = (): string => {
if (import.meta.env.DEV) {
// 開發環境利用 Vite proxy,走相對路徑
return '/api';
}
// 生產環境直接指向實際後端服務網域
return import.meta.env.VITE_PRODUCTION_API_URL || 'https://api.example.com';
};
export const fetchUserData = async (userId: string) => {
const baseUrl = getApiBaseUrl();
const response = await fetch(`${baseUrl}/users/${userId}`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
});
if (!response.ok) {
throw new Error(`API 請求失敗,狀態碼:${response.status}`);
}
return response.json();
};
這個範本在開發環境走 /api,由 Vite proxy 轉發,能正常取得資料。建置後改成直接呼叫後端網址,就是真正的跨來源請求,後端必須自己處理 CORS,而且要留意這支 fetch 帶了 Content-Type: application/json:MDN 說明簡單請求的 Content-Type 只允許 application/x-www-form-urlencoded、multipart/form-data、text/plain,其他值會讓瀏覽器先對後端送出 OPTIONS 預檢請求,後端的預檢回應也要用 Access-Control-Allow-Headers 列出 content-type。後端只回 Access-Control-Allow-Origin 而沒有處理預檢時,瀏覽器會在 Console 顯示 Request header field content-type is not allowed by Access-Control-Allow-Headers in preflight response,畫面見下圖的最後一列。

三、 預檢請求與連線問題排查步驟
遇到「開發時正常、部署後失敗」或「代理設定了卻沒作用」時,依序從下面三步往外縮小範圍。
- 先不經過瀏覽器,直接對開發伺服器發請求,確認 Vite proxy 本身有沒有轉發成功。Windows PowerShell 中請輸入
curl.exe,避免被別名curl攔截。狀態碼 502 表示代理連不上或逾時,Vite 終端機會同時印出http proxy error與原因;404 則先檢查rewrite後的路徑。
curl.exe -i http://localhost:5173/api/users/7
- 直接對後端模擬瀏覽器的預檢請求,看回應有沒有
Access-Control-Allow-Origin與Access-Control-Allow-Headers。以下api.example.com請換成實際的後端網址,Origin換成前端的正式網址。
curl.exe -i -X OPTIONS https://api.example.com/users/7 `
-H "Origin: https://app.example.com" `
-H "Access-Control-Request-Method: GET" `
-H "Access-Control-Request-Headers: content-type"
回應若沒有 Access-Control-Allow-Headers: content-type,就是上一節遇到的預檢失敗;有標頭但狀態碼不是 2xx,則要改查後端對 OPTIONS 方法的處理。
- 確認正式主機本身有轉發規則。
vite preview沿用server.proxy,它正常不代表正式環境正常;正式環境要由 Nginx 等 Web 伺服器轉發,或像上一節那樣直接指向有 CORS 設定的後端。
四、 常見問題與排錯(Q&A)
Q1:vite proxy in production 環境中為何失效?建置後如何替代本地代理?
- 問題原因:Vite 提供的
server.proxy是開發伺服器的功能。官方文件指出,若要在內部的 connect 應用加入自訂中介層,要寫成使用configureServer的外掛。一旦執行vite build產出靜態檔案並部署至靜態主機,代理邏輯就不存在了。 - 解法:在生產環境中,應改由正式的 Web 伺服器(如 Nginx 反向代理)處理
/api請求的轉發,或在前端程式碼中透過環境變數(import.meta.env.VITE_API_URL)直接指向實際後端網址,後端則要設定好 CORS,作法見第二節。
Q2:本機用 vite preview 看起來正常,部署後 API 卻 404,是哪裡不同?
- 問題原因:
preview.proxy預設值就是server.proxy,本機預覽沿用了開發時的代理設定;部署後的靜態主機沒有這層代理,相對路徑/api/...會直接打到靜態主機本身。 - 解法:依第三節的步驟 3,確認正式主機有轉發規則,或把前端改成呼叫有 CORS 設定的後端網址。
Q3:部署後 Console 出現 not allowed by Access-Control-Allow-Headers in preflight response,該改前端還是後端?
- 問題原因:
fetch帶了Content-Type: application/json,不屬於簡單請求允許的值,瀏覽器會先送OPTIONS預檢;後端的預檢回應沒有用Access-Control-Allow-Headers列出content-type。 - 解法:改後端,讓
OPTIONS請求回應Access-Control-Allow-Origin與Access-Control-Allow-Headers: content-type。可用第三節步驟 2 的curl.exe指令先確認回應標頭,再回到瀏覽器驗證。
參考資料與延伸閱讀
- 〈Vite proxy 開發伺服器轉發設定、CORS 繞過與 Production 部署排查〉
- 〈React 工具鏈規格解析:基於 Vite 的初始化流程、目錄架構與生產環境建構〉
- 〈CORS 錯誤是什麼? 如何解決本地端跨域請求 CORS Error〉
- Vite 官方文件:Server Options – server.proxy
- Vite 官方文件:Preview Options – preview.proxy
- Vite 官方文件:Env Variables and Modes
- Cross-Origin Resource Sharing (CORS) – HTTP | MDN
- Same-origin policy – Security | MDN
發佈留言