紀錄工作經驗、相關知識,解決技術相關問題。

React, 工作雜談

解決Next.js中圖片CORS問題:一個常見錯誤的快速修復

解決Next.js中圖片CORS問題:一個常見錯誤的快速修復 特色圖片

開發 Next.js 應用程式時,處理來自外部網域的圖片資源經常會遇到跨源資源共享的阻礙。Next.js CORS 問題通常發生在我們嘗試對外部圖片執行 Canvas 操作、套用特定安全策略,或在原生標籤中使用跨域屬性時。想了解基礎 CORS 運作機制與瀏覽器安全策略細節,可先參考站內的 CORS 錯誤是什麼? 如何解決本地端跨域請求 CORS Error 教學。當我們在專案中使用原生 <img> 標籤並加上 crossorigin="anonymous" 屬性時,若圖片伺服器未正確回應對應的標頭,往往會導致圖片載入失敗。本文將解析此問題的成因,並提供透過 next.config.js 設定與後端標頭調整的具體解決方案。

一、 問題成因與規格對照

在 Next.js 專案中,當開發者試圖對外部圖片執行 Canvas 操作或套用特定安全策略而加上 crossorigin="anonymous" 屬性時,若遠端伺服器未正確設定 CORS 標頭,就會觸發阻擋機制。

crossorigin 屬性規格對照表

屬性值描述說明伺服器端需求
anonymous發送不帶用戶憑證的跨域請求。伺服器需回應 Access-Control-Allow-Origin。
use-credentials發送包含 Cookie 等憑證的請求。伺服器需明確允許特定來源與憑證。
無屬性不強制套用 CORS 檢查規則。適用於一般靜態資源載入。

二、 核心解法與程式碼實作

為了充分發揮框架效能並避免跨域衝突,建議優先使用 Next.js 內建的 <Image> 元件,並在設定檔中明確宣告允許載入的遠端圖片來源。若必須使用原生標籤或處理底層請求,則需同步調整伺服器端標頭。

1. next.config.js 的 remotePatterns 設定

當圖片來自外部網域(例如 CDN 或第三方圖床)時,必須在 next.config.js 中進行設定,讓框架能夠安全地抓取並進行優化:

/** @type {import('next').NextConfig} */
const nextConfig = {
  images: {
    remotePatterns: [
      {
        protocol: 'https',
        hostname: 'example.com',
        port: '',
        pathname: '/images/**',
      },
    ],
  },
}

module.exports = nextConfig

2. 伺服器端(Node.js / Express)CORS 標頭設定

若專案必須使用原生 <img> 標籤並啟用 crossorigin="anonymous",圖片所在的伺服器必須在 HTTP 回應中帶入正確的標頭。以下為使用 Express 框架設定跨域回應的範例:

const express = require('express');
const app = express();

// 設定全局或特定路由的 CORS 標頭
app.use('/images', (req, res, next) => {
  res.setHeader('Access-Control-Allow-Origin', '*');
  res.setHeader('Access-Control-Allow-Methods', 'GET, OPTIONS');
  res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With, content-type');
  next();
});

app.use('/images', express.static('public/images'));

app.listen(3000, () => {
  console.log('Image server running on port 3000');
});

三、 部署與維運的 2 大隱藏踩坑點

在將 Next.js 專案部署至生產環境時,除了正確設定基本的跨域標頭外,還必須注意以下兩項常被忽略的邊界條件,以確保圖片資源在各種網路環境下都能穩定載入。

1. CDN 快取汙染與 Vary: Origin 標頭遺漏

  • 問題原因:當圖片伺服器(如 Cloudflare、CloudFront 等 CDN)直接回應 Access-Control-Allow-Origin: * 給帶有 Origin 標頭的跨域請求時,CDN 往往會將這份回應連同 CORS 標頭一併快取起來。若後續有另一個不帶 Origin 標頭(或來自不同網域)的請求存取同一 URL,CDN 可能會直接回傳帶有舊跨源標頭的快取,導致某些安全性要求嚴格的瀏覽器發生未預期的阻擋或快取錯亂。
  • 解法:圖片伺服器在回傳動態 CORS 標頭時,務必同時設定 Vary: Origin 回應標頭。這能強制 CDN 根據不同的 Origin 請求標頭分開快取,避免跨網域快取汙染:
    res.setHeader('Access-Control-Allow-Origin', req.headers.origin || '*');
    res.setHeader('Vary', 'Origin'); // 關鍵防呆設定

2. 混合內容與 HTTPS 協定強制對齊

  • 問題原因:在本地開發環境(http://localhost:3000)測試順利,但一上線到正式 HTTPS 環境時,Next.js 的 <Image> 或原生 <img> 請求外部 HTTP 圖片資源,遭到瀏覽器直接阻擋並拋出 CORS 或者是 Mixed Content 錯誤。許多開發者誤以為是伺服器 CORS 標頭遺失,實際上是瀏覽器全面禁止安全網頁(HTTPS)載入不安全來源(HTTP)。
  • 解法:檢查 next.config.js 的 remotePatterns 宣告中,protocol 是否嚴格指定為 https;同時,遠端圖片伺服器所輸出的圖片絕對網址也必須全面強制使用 HTTPS,避免前端動態拼接出 HTTP 網址。

四、 常見問題 (FAQ)

Q1:為什麼加上 crossorigin="anonymous" 後,部分圖片會無法顯示?

當瀏覽器偵測到跨域請求帶有 crossorigin 屬性時,會強制發送 Preflight(預檢請求)並檢查伺服器回傳的 Access-Control-Allow-Origin。若伺服器未回應該標頭,瀏覽器基於安全性會直接阻擋資源載入。

Q2:使用 Next.js 的 Image 元件還需要設定 CORS 嗎?

不需要。Next.js 的優化機制是透過伺服器端代理抓取外部圖片,因此不會受到瀏覽器端 CORS 政策的直接限制,開發者只需要在 next.config.js 設定 remotePatterns 即可。

Q3:如何驗證圖片伺服器是否正確回傳標頭?

可以透過瀏覽器的開發者工具(F12)切換至 Network 分頁,點擊該圖片請求,並檢查 Response Headers 中是否包含 access-control-allow-origin。

總結

理解 CORS 機制與瀏覽器安全策略是現代網頁開發的基礎。透過正確配置 Next.js 的遠端圖片來源,或是確保圖片伺服器回傳合適的標頭,能夠有效排除跨域載入失敗的問題,確保應用程式穩定運行。

參考資料與延伸閱讀

發佈留言