問題情境

專案部署到 IIS 的虛擬目錄,URL 結構如下:

  • 本機:http://localhost:56961/Project/
  • 正式環境:https://www.project.com.tw/Project/

HTML 裡的圖片路徑寫 ./Content/images/image.png,本機跑得好好的,上正式就全破圖。

實際原因./ 是相對路徑,參考點是「解析該路徑的檔案位置」,不是 HTML 頁面。若圖片路徑寫在外部 .css 檔(例如放在 /Project/styles/main.css),瀏覽器會以 CSS 檔的目錄為基準,把 ./Content/images/image.png 解析成 /Project/styles/Content/images/image.png,而非你預期的 /Project/Content/images/image.png

換部署位置(從本機 localhost 換到子目錄或反向代理後方)時,CSS 檔的實際 URL 路徑可能跟著變,就會踩到這個問題。

三種解法

方案一:硬寫絕對路徑

1
background: url("/Project/Content/images/image.png");

開頭的 / 讓瀏覽器從網域根目錄算起,不受 CSS 檔或 HTML 檔的位置影響。

真正的缺點:路徑硬寫了 /Project/ 這個子目錄前綴。換部署位置(例如從 /Project/ 移到根目錄)時,要全站搜尋替換所有絕對路徑。適合子目錄名稱永遠不變的長期專案。

方案二:ASP.NET MVC URL Helper

@Url.Content() 只能在 Razor 的 .cshtml 裡執行,不能放在外部 .css 靜態檔。正確用法是在 Razor layout 或 view 的 <style> 區塊,或直接用在 <img>src 屬性:

1
2
3
4
5
<!-- 在 .cshtml 的 <img> 標籤 -->
<img src="@Url.Content("~/Content/images/image.png")" alt="..." />

<!-- 或在 .cshtml 的 inline style -->
<div style="background: url('@Url.Content("~/Content/images/image.png")')">

~/ 代表應用程式根目錄,ASP.NET 會自動補上 /Project/ 前綴,換部署路徑時不需要改程式碼。

缺點:只限 Razor 視圖能用;外部 .css 靜態檔完全無效。

方案三:設定 HTML <base> tag(推薦)

1
2
3
4
<head>
<base href="/Project/" />
<!-- 其他 head 標籤 -->
</head>

設了 <base> 後,頁面上所有相對路徑(包括從外部 CSS 的 url() 繼承效果之外、HTML 裡的 srchref)都以 base href 為起點計算。只要在 layout 改一個地方,換部署路徑時也只改這一行。

使用前必須知道的限制

  1. 頁內錨點會壞掉<a href="#section"> 會被解析成 https://www.project.com.tw/Project/#section,瀏覽器會先離開當前頁面再跳回來,而非在頁內捲動。有目錄或錨點跳轉的頁面,必須把所有錨點改成完整絕對 URL(<a href="/Project/page.html#section">)。

  2. SPA 路由需要額外處理:用 history.pushState 或 hash 路由的 SPA 框架(Vue Router、React Router)必須設定對應的 base 選項,否則路由計算會偏移。

  3. 必須放在 <head> 最前面<base> 要在任何使用相對路徑的 <link><script><img> 之前出現,否則已解析過的相對路徑不會重新計算。

最佳實踐

按場景選工具,別只看優點:

  • 純靜態 HTML 站,部署到子目錄:用 <base> tag,是最省事的方案,但先確認頁面沒有頁內錨點。
  • ASP.NET MVC 且圖片路徑在 Razor 模板裡:用 @Url.Content("~/..."),讓框架幫你算路徑。
  • 現代前端工具鏈(Vite、Webpack):設定 base(Vite)或 publicPath(Webpack);打包時工具會自動替換所有資源路徑,不需要手動維護任何方案一或方案三。
  • 絕對路徑方案適合最簡單的情況:部署路徑固定且不使用框架。

最根本的預防方式:在專案初期就確定部署路徑的結構,避免本機跑根目錄、正式環境跑子目錄這類不一致的情境。

參考資料

  1. MDN - The base element
  2. ASP.NET MVC - UrlHelper.Content 方法
  3. W3Schools - HTML base Tag