一位顧問把名片上的乾淨網址——truelink-group.com/johnny——分享給客戶。客戶用手機一打開,畫面卻先閃出一個大大的「404」,隔了一瞬間才跳到正確的顧問落地頁。顧問緊張地問我們:「是不是我的連結壞了?」我們在自己電腦上反覆測試,怎麼開都很順、根本看不到 404。直到我們改用手機、特別是從社群 App 裡點進去,那一閃而過的 404 才現身。這不是連結壞了,而是一個藏在「裸短網址沒有後端路由」與「前端補救轉址放錯位置」之間的可見副作用。這篇文章,是我們在 TrueLink 自家平台上實際遇到、診斷、並修好這個坑的完整第一手紀錄——也順帶把背後「歸因路由」這個看似多餘、其實刻意的設計取捨講清楚。

本文重點

  1. 症狀:手機上先閃一下 404 才跳轉
  2. 先搞懂:裸 vanity 網址與歸因路由是兩種東西
  3. 根因:沒有路由就回 404,補救 script 又放在最末端
  4. 為什麼桌機看不到、手機才明顯
  5. 修法:把轉址上移 head、先藏畫面再 replace
  6. 迴圈防護:nf=1 為什麼不可省
  7. 為什麼不用 catch-all rewrite 一勞永逸
  8. 歸因路由的取捨:誰把這個客戶帶進來的?
  9. first-touch 歸因 cookie 的取捨
  10. 為什麼一閃而過的 404 仍然值得修
  11. 上線前檢查清單

症狀:手機上先閃一下 404 才跳轉

先還原現場。我們的顧問有一個專屬的乾淨推廣網址,形式是裸單段路徑——例如 truelink-group.com/johnny。它印在名片上、寫在報價單裡、貼進私訊裡,目的就是「好記、好念、好打」。客戶拿到這個網址,在手機上輸入或點開它,期待看到的是顧問的個人介紹落地頁。

實際發生的是:網頁打開的瞬間,畫面上出現一張全頁的「404 找不到頁面」大標題,停留極短的一瞬,接著才自動跳轉到正確的顧問落地頁。整個過程雖然最後會到達正確的目的地,但那「閃一下 404」的瞬間,已經在客戶心裡種下一個問號。

最折磨人的,是它難以在開發環境重現。工程師在自己的桌機上,用 Chrome、Safari 反覆打開那個裸網址,幾乎每次都是「咻」地一下就到落地頁,看不到任何 404。於是這類回報很容易被當成「使用者眼花」或「偶發網路問題」而被擱置——直到越來越多手機使用者,尤其是從 LINE、Facebook 這類 App 內建瀏覽器點進來的人,描述出同一個畫面。

先搞懂:裸 vanity 網址與歸因路由是兩種東西

要理解這個 bug,得先分清楚兩個長得很像、但職責完全不同的網址:

換句話說,/r/johnny 是「給機器與正式分享用的、結構完整的入口」,而 /johnny 只是「給人類好記的乾淨外觀」。理想情況下,我們希望使用者打 /johnny 也能順利抵達顧問落地頁——而這正是問題發生的接縫處。

關鍵設計觀念:對外正式分享(貼到社群、訊息)應該用 /r/johnny,因為它對爬蟲與分享預覽友善;裸 /johnny 是給名片這種「人會親手輸入」的場景。對沒有執行 JavaScript 的爬蟲而言,裸網址仍然是 404——這是一個刻意的取捨,不是疏漏。

根因:沒有路由就回 404,補救 script 又放在最末端

現在把鏡頭拉到伺服器這一端,看看當客戶打 /johnny 時,到底發生了什麼。

第一件事:裸單段路徑 /johnny 沒有對應的後端路由(rewrite)。我們的主機只為正式的歸因路由設了規則(/r/** 會交給後端的轉址函式處理),但「裸的單段路徑」並不在這份規則裡。於是主機去找一個叫 /johnny 的實體頁面,找不到。

第二件事:找不到頁面,主機就做它該做的事——回傳真正的 404 頁面(HTTP 狀態碼就是 404)。這個 404 頁面是一張設計好的、多語系的「找不到頁面」錯誤頁,本身沒有問題。

第三件事,也是 bug 的真正所在:為了不要讓客戶卡死在 404,我們在這個 404 頁面裡放了一段補救轉址 script。它的邏輯是:「如果使用者打進來的是一個合法格式的單段 slug(像 johnny),那就把他導去正式的歸因路由 /r/johnny。」這個想法本身完全正確——但那段 script 原本被放在頁面 <body> 的最末端

瀏覽器處理一份 HTML 的順序,是由上而下解析的。當轉址 script 在 <body> 末端時,瀏覽器會先把整個頁面的內容解析、繪製出來(包括那個又大又醒目的「404」標題),之後才執行到底部那段 script,才開始做 location.replace('/r/johnny') 的轉址。於是順序變成了:

  1. 主機回 404 頁面。
  2. 瀏覽器解析並繪製出「404」大標題(使用者看到了)。
  3. 瀏覽器執行到 body 末端的 script,發現是合法 slug,呼叫 location.replace
  4. 頁面跳轉到 /r/johnny,正確的落地頁出現。

第 2 步與第 3 步之間的那個空檔,就是使用者眼中「閃一下 404」的真相。它不是壞掉,是一個先繪製、後轉址的時間差被肉眼捕捉到了。

為什麼桌機看不到、手機才明顯

這就解釋了那個最令人困惑的現象:為什麼同一個網址,在工程師的電腦上怎麼測都正常,到了客戶手機上卻會閃?

答案在於那個「空檔」的長度,會隨著裝置與網路條件而伸縮。桌機通常 CPU 較快、網路較穩定,從「繪製 404」到「執行底部 script」之間的時間極短,短到肉眼幾乎無法分辨——畫面看起來就是一步到位。

但手機不同。行動裝置的處理速度與連線品質普遍較弱,尤其是從社群 App 的內建瀏覽器點進來時,環境更受限。那個原本短到看不見的空檔被放大,於是 404 大標題被真真切切地畫了出來,停留一瞬,才被轉址洗掉。這也是為什麼這類 bug 特別愛在「客戶的真實手機」上現形,卻在「開發者的乾淨環境」裡躲得好好的。

這裡有一個放諸四海皆準的教訓:任何「先讓某個過渡狀態被繪製、再用 JavaScript 把它換掉」的設計,在慢一點的裝置上都會露出那個過渡狀態。真正穩健的做法,是讓那個你不想被看到的狀態,根本沒有機會被繪製出來。

修法:把轉址上移 head、先藏畫面再 replace

知道了根因,修法就清晰了。核心只有一句話:讓轉址發生在 404 大標題被繪製出來之前。我們做了兩件事:

1. 把補救轉址 script 上移到 <head> 的最前面

我們把那段轉址邏輯,從 <body> 末端搬到 <head> 的最前端,也就是 CSS 與頁面內容還沒被繪製的時候就先執行。如此一來,瀏覽器在還沒畫出任何 404 視覺之前,就有機會判斷「這是不是一個該轉址的裸 slug」。

2. 命中 slug 時,先把整頁藏起來,再 replace

光是上移還不夠保險——在轉址真正生效的那幾毫秒裡,理論上仍可能閃出一點點內容。所以我們加上一道保險:當 script 判斷這是一個合法的單段 slug 時,documentElementvisibility 設成 hidden(在任何繪製之前,先把整頁藏起來),location.replace 導去 /r/johnny。因為頁面被藏住,使用者連那一瞬間的殘影都看不到,達到真正的零閃爍

修好後的執行順序變成:

  1. 主機回 404 頁面。
  2. 瀏覽器一開始解析 <head>立刻執行轉址 script。
  3. script 判斷是合法 slug → 先 visibility:hidden 藏頁 → location.replace('/r/johnny')
  4. 使用者完全沒看到 404,直接抵達落地頁。

還有一個重要的安全網:如果轉址因為任何原因失敗,就照常顯示 404,不會把使用者卡死在一張藏起來的空白頁。換句話說,這層補救只在「能成功幫上忙」時介入,幫不上忙時則優雅退場、回到原本正常的 404 行為。

同樣重要的是,這個修法零路由風險——我們完全沒有動主機的 rewrite 規則,只調整了 404 頁面內那段 script 的位置與行為。改動的爆炸半徑被壓到最小:它只影響「本來就會 404 的請求」,不可能波及任何正常頁面。

迴圈防護:nf=1 為什麼不可省

把裸路徑導去歸因路由,會引出一個必須處理的邊界情況:如果這個 slug 根本不存在呢?

想像一下:客戶打了 /johndoe,但平台上根本沒有叫 johndoe 的顧問。我們的補救 script 看到它是合法格式的單段 slug,照規則把他導去 /r/johndoe。後端的歸因路由一查,查無此人,於是要把使用者彈回 404 頁——而 404 頁裡的補救 script 又看到 johndoe 是合法 slug,又把他導去 /r/johndoe……如果不處理,這就成了兩邊互踢的無限轉址迴圈

解法是一個簡單而關鍵的迴圈防護旗標:當歸因路由查無 slug、要把使用者彈回 404 時,會在網址尾巴帶上一個 nf=1 參數(nf 可理解為 "not found")。而 404 頁裡的補救 script,在做任何轉址之前,會先檢查網址裡是不是已經有 nf=1

這一個小小的旗標,把「合法格式但不存在的 slug」這個尷尬情況收得乾乾淨淨:使用者最多被導一次,確認查無資料後就停在一個正常的 404,而不會被困在無止盡的跳轉裡。任何「A 導去 B、B 在某條件下又導回 A」的轉址設計,都必須有一個這樣的單向旗標來打破潛在迴圈——這是寫轉址邏輯時不能省的一課。

為什麼不用 catch-all rewrite 一勞永逸

讀到這裡,很多工程師會冒出一個直覺:「與其在 404 頁裡用前端 script 補救,為什麼不乾脆加一條 catch-all 的 /* rewrite,把所有裸路徑都送進後端,由後端統一判斷要不要轉址?這樣不就根本不會回 404、也不會閃了嗎?」

這個想法很合理,但我們刻意選擇不這麼做,原因是成本與穩定的取捨:

所以我們的原則是:不動 rewrites,只在「本來就是 404」的請求上做前端補救。乾淨的 catch-all 看似一勞永逸,真實代價卻是把每一個 typo 都升級成一次後端呼叫。把改動限制在最小範圍——只在已經要失敗的請求上補救、不碰正常流量的路由——才是更穩健、更省成本的選擇。

推廣連結、登入、轉換的「看不見的破口」,想要有人幫你把關?

從裸短網址的 404 閃爍,到歸因路由與整體獲客動線的順暢度,這些藏在接縫處的細節往往最傷信任與轉換。TrueLink 可以協助你檢視自家產品的連結、登入與歸因設計,把放血的傷口補起來。

預約數位顧問諮詢 與我們聯絡

歸因路由的取捨:誰把這個客戶帶進來的?

講到這裡,值得退一步問:我們為什麼一開始要設計 /r/johnny 這條歸因路由?為什麼不直接讓裸 /johnny 變成正式入口就好?

因為「誰把這個客戶帶進來的」這件事,本身就有商業價值。當一位顧問用他的專屬網址把客戶帶進平台、客戶最後註冊或成交,平台需要知道「這個轉換要歸功給哪位顧問」。/r/johnny 這條路由,本質上就是一個帶著歸因資訊的正式入口:它由後端處理,可以在伺服器端就把「這次造訪來自 johnny」這件事記錄下來、把對的歸因資訊一路帶進後續的註冊流程。

這就帶出一組真實的工程取捨:

我們的解法不是二選一,而是讓兩者並存、各司其職:把裸網址留給「人會親手輸入」的名片場景,並用前述的補救轉址把它無痛接到歸因路由;而把 /r/johnny 當成「對外正式分享」的標準連結。這樣顧問既能在名片上印出乾淨好記的 /johnny,平台又不會在客戶轉換時丟失「是誰帶進來的」這個關鍵歸因。這個 404 閃爍的 bug,其實正是這套「人類友善網址」與「機器友善路由」並存設計在接縫處的一道裂縫——修好它,才讓這套取捨真正完整。

歸因還有一個更細的維度:當客戶不是「點進來就立刻註冊」,而是「先看看、過幾天才回來註冊」時,平台要怎麼記住「他最初是被誰帶進來的」?這就牽涉到所謂的 first-touch(首次接觸)歸因,而它通常靠一個 cookie 來實現——在客戶首次經由顧問連結造訪時,就把「來源是 johnny」寫進瀏覽器的 cookie,往後就算他直接打開網站再註冊,也能讀回這份來源、把功勞正確歸給最初帶他來的人。

這個機制很實用,但它帶著一串必須誠實面對的取捨。以下是設計 first-touch 歸因 cookie 時要權衡的幾個面向:

我們把這些取捨攤開講,是因為歸因常被當成「裝一下就好」的小功能,但它一旦牽涉到把功勞(乃至於分潤)歸給某個人,每一個旋鈕——記第一次還是最後一次、記多久、存什麼——都會變成真實的公平性與信任問題。誠實面對歸因的誤差與選擇,比假裝它精準無比更值得信賴。

為什麼一閃而過的 404 仍然值得修

你可能會想:「不過就是閃一下,最後還是會到正確頁面,有必要大費周章嗎?」答案是:有,因為在信任這門生意裡,第一印象很貴。

把場景想清楚:顧問把乾淨網址印在名片上、寫進報價單、貼進私訊,鄭重地分享給一位還在評估要不要信任這家公司的潛在客戶。客戶第一次打開這個連結——也就是他與這個品牌的第一次數位接觸——畫面卻先給他一個又大又醒目的「404」。即使隨後跳轉成功,那一瞬間傳達的潛台詞是:「這個連結怪怪的」「這家公司的東西不太穩」。對一個賣「數位信任」的品牌來說,這種首觸瑕疵尤其諷刺,也尤其傷。

而它最危險的地方,跟許多手機端的轉換問題一樣,是安靜。客戶不會特地回報「我看到你網站閃了一下 404」,他只會默默地在心裡扣一分,甚至直接關掉。你這邊的日誌裡,可能只是少了一筆完成的造訪,沒有任何錯誤訊號告訴你「剛剛有人在第一哩路上對你產生了懷疑」。我們之所以會發現它,正是因為一位願意開口的顧問替他的客戶問了一句——有多少不開口的人,已經默默扣了分?

反過來說,這也意味著:補這個破口,是少數「投入很小、回報直接」的體驗優化。我們沒有重寫路由、沒有動全域 rewrite、沒有承擔任何後端成本,只是把一段 script 換了位置、加了一道藏畫面與一道迴圈防護。對的人,在第一次打開連結時,就看到順暢、乾淨、可信賴的落地頁——順暢的入口,本身就是數位信任最具體的一塊基石。

上線前檢查清單

最後,把整套做法收斂成一份可以直接拿去對照的清單:

這個問題的本質,從來不是「連結壞了」,而是「一個你不想被看到的過渡狀態,在慢一點的裝置上被畫了出來」。真正的解法不是事後把它洗掉,而是讓它根本沒有機會被繪製。把這個接縫補好,你守住的不只是一個短網址,而是客戶與你品牌第一次相遇時那份還很脆弱的信任。