有一天,一位客戶傳訊息給我們:「你們網站的 Google 登入是不是壞了?我按下去整個畫面都變成 Google 的錯誤頁。」我們第一時間在自己的手機、自己的電腦上反覆測試,怎麼按都正常。直到我們追問一句:「你是不是從 LINE 裡面點我們的連結進來的?」——對,他是。這就是整起事件的關鍵。客戶看到的是 Google 滿版的「存取遭封鎖(Access blocked)」與 403 disallowed_useragent 錯誤,而這完全不是我們網站、DNS 或 Firebase 設定的問題。這是 Google 與 Apple 出於安全考量,刻意封鎖在 App 內建瀏覽器(In-App Browser / WebView)裡執行 OAuth 登入所造成的。它影響全世界每一個網站的 Google 登入,而且絕大多數使用者根本不知道自己正身處「內建瀏覽器」之中——他們只是在 LINE 或 Facebook 裡點了一個連結。這篇文章,就是我們在 TrueLink 自家平台上實際遇到、診斷、並修好這個問題的完整第一手紀錄。
本文重點
症狀:客戶看到 Google「存取遭封鎖」滿版錯誤
先還原現場。一位潛在客戶在 LINE 群組或一對一聊天裡,看到有人分享了我們的網站連結。他在 LINE 裡點了那個連結,網站順利打開、內容也正常顯示——到這裡一切都好。接著他想註冊或登入,按下了畫面上那顆熟悉的「使用 Google 帳號登入」按鈕。
然後,不是跳出 Google 的帳號選擇畫面,而是整個視窗被一張 Google 的滿版錯誤頁佔據,上面寫著:
存取遭封鎖:這個應用程式的要求無效(Access blocked: This app's request is invalid)
錯誤代碼 400:disallowed_useragent
有些版本顯示的是「403 disallowed_useragent」或「您無法登入這個應用程式,因為它不符合 Google 的安全政策」。對使用者來說,這個畫面是恐怖而且勸退的:它看起來像是你的網站不安全、被 Google 列為危險、或者根本是個釣魚網站。在那個瞬間,你失去的不只是一次登入,而是這位客戶對你品牌的信任。
我們在 TrueLink 自家平台上實際遇到的,正是這一幕。最折磨人的地方在於:我們完全無法在開發環境重現它。工程師用 Chrome、Safari、Firefox 測,全部正常;用手機的 Safari、Chrome 測,也全部正常。只有當你真的從 LINE 或 Facebook 裡點連結進來,才會觸發。這種「只在野外發生、實驗室裡永遠複製不出來」的 bug,最容易被誤判為偶發、被擱置,最後變成一個持續放血的傷口。
根因:Google 與 Apple 封鎖 WebView 內的 OAuth
釐清一件最重要的事:這不是你的錯,也不是任何單一網站的 bug。它不是 DNS 設定錯誤、不是 Firebase 專案憑證問題、不是 OAuth client ID 填錯。你可以把整個 auth 設定翻過來檢查三遍,都找不到問題——因為問題根本不在那裡。
真正的原因是:Google 從大約 2021 年起,明文禁止在「嵌入式 WebView(embedded user-agent)」中執行 OAuth 授權流程,Apple 的 Sign in with Apple 也有類似限制。所謂的嵌入式 WebView,就是 App 自己內建的迷你瀏覽器——當你在 LINE、Facebook、Instagram、Messenger、WeChat(微信)、TikTok 裡點開一個外部連結時,這些 App 多半不會把你丟到系統的 Chrome 或 Safari,而是用一個包在 App 內部的瀏覽器視窗來顯示網頁。這就是「In-App Browser(內建瀏覽器)」。
Google 封鎖它的理由是安全:在嵌入式 WebView 裡,宿主 App 理論上可以注入 JavaScript、攔截 cookie、讀取你輸入的帳號密碼。為了保護使用者的 Google 帳號不被惡意 App 透過這層中介竊取,Google 乾脆規定:OAuth 登入只能在「完整、獨立的瀏覽器」中進行,凡是偵測到自己跑在 WebView 裡,就直接拒絕,回傳 disallowed_useragent。
這裡有一個關鍵的認知落差,也是這個問題之所以「隱形」的核心:絕大多數使用者根本不知道自己身處 In-App Browser。他們的心智模型很單純——「我在用 LINE,我點了一個連結,網頁打開了」。他們不會意識到這個網頁是在一個「不是真正瀏覽器」的容器裡跑的。所以當登入失敗時,他們的歸因永遠是「這個網站爛掉了」,而不是「我應該換個瀏覽器打開」。這個落差,正是你必須主動介入、主動引導的原因。
為什麼 popup 與 redirect 後備全都失靈
很多開發者第一個直覺反應是:「那我加個後備機制不就好了?」這正是我們一開始走過的彎路,所以這裡要特別講清楚,幫你少走一遭。
常見的 Firebase / Google OAuth 登入有兩種啟動方式:
signInWithPopup:彈出一個新視窗讓使用者選 Google 帳號。signInWithRedirect:把整個頁面導向 Google 的登入頁,登完再導回來。
很自然地,你會想:如果 popup 在 WebView 裡被擋,那我 catch 到錯誤之後自動 fallback 到 redirect,總該成功了吧?
不會。兩條路都死在同一個地方。原因很簡單:無論是 popup 還是 redirect,它們最終都還是在同一個被封鎖的 WebView 環境裡執行。Google 的封鎖判斷是看「請求來自哪種 user-agent」,而不是看「你用 popup 還是 redirect 觸發」。WebView 就是 WebView,換個觸發方式,宿主環境並沒有改變,Google 依然回你 disallowed_useragent。
所以結論很硬:在程式碼層、在 auth 流程裡,你想不出任何辦法讓 OAuth 在 WebView 裡成功。這不是寫得夠不夠巧的問題,是 Google 設計上就不允許。當我們在自家平台上反覆撞牆、把 popup、redirect、各種 retry 組合都試過一輪之後,才真正接受一個事實:這個問題的解,根本不在 auth 層。
真正的解法是 UX,不是改 auth
既然無法讓 OAuth 在 WebView 裡跑通,那唯一的出路就是:在使用者按下那顆注定失敗的按鈕之前,攔住他,把他引導到一個會成功的環境。
換句話說,這是一個 UX(使用者體驗)問題,不是一個 authentication(驗證)問題。我們在 TrueLink 落地的完整策略,可以濃縮成三步:
- 偵測:在頁面載入時,透過 User-Agent 判斷使用者是否正身處某個已知的 In-App Browser。
- 攔截:如果是,就不要讓「用 Google 登入」「用 Apple 登入」這兩顆按鈕去觸發那個注定 403 的 OAuth 流程。改成攔截點擊事件,跳出一段引導。
- 引導:清楚告訴使用者「請改用外部瀏覽器開啟此頁面」,並提供盡可能無痛的方式幫他做到(複製連結、Android 直接喚起 Chrome、各 App 的選單提示)。
這個心態轉變是整件事的核心:你不是在修一個壞掉的登入按鈕,你是在補一個體驗的破口。使用者不知道自己在 WebView 裡,那就由你——這個唯一知道真相的一方——溫柔地告訴他、並牽著他走出去。這正是 TrueLink 一直強調的:順暢、可信賴的登入,本身就是數位信任的一部分。一個會在客戶面前無聲爆掉的登入流程,傷害的是整個品牌的可信度。
如何偵測 In-App Browser:User-Agent 簽章
偵測的依據是瀏覽器送出的 User-Agent 字串。各大 App 的內建瀏覽器,都會在 UA 裡留下自己的簽章。以下是我們在實務上實際用來比對的主要簽章(這份清單建議定期回顧,因為 App 偶爾會調整 UA):
- LINE:UA 含
Line/ - Facebook:UA 含
FBAN、FBAV或FB_IAB(FB In-App Browser) - Instagram:UA 含
Instagram - Messenger:UA 含
Messenger - WeChat(微信):UA 含
MicroMessenger - TikTok:UA 含
BytedanceWebview或musical_ly - KakaoTalk:UA 含
KAKAOTALK - Threads:UA 含
Barcelona(Threads 的開發代號) - Android 系統 WebView(泛用):UA 含
; wv)這個標記
實作上,做法是在頁面早期取得 navigator.userAgent,逐一比對上述簽章。命中任一條,就把這個 session 標記為「身處 In-App Browser」,後續據此調整 OAuth 按鈕的行為。實作原則很重要:偵測只在「決定要不要顯示引導」這一步使用,不要拿它去做任何安全性判斷——UA 可被偽造,它只適合用在這種「優雅降級」的 UX 場景。
我們建議把這份簽章清單抽成一個獨立、可單元測試的小函式(例如 isInAppBrowser(ua)),餵入各家 App 的真實 UA 範例做測試。這樣當你要新增一個 App 或修正一條規則時,回歸測試能保護你不會誤傷既有判斷。
保守偵測:絕不誤判真實瀏覽器
偵測規則最危險的失敗模式,不是「漏掉某個 In-App Browser」,而是「誤把真正的瀏覽器當成 In-App Browser」。想想看:如果你的規則太寬鬆,把一位用 Safari、Chrome、Firefox 或 Samsung Internet 的正常使用者誤判了,你就會無緣無故攔下他、叫他「改用外部瀏覽器開啟」——但他明明已經在外部瀏覽器裡。這種誤判會把一個本來能順利登入的人,硬生生變成困惑而流失的人。誤判一個真實瀏覽器的代價,遠高於漏判一個 In-App Browser。
所以偵測必須保守(conservative),原則如下:
- 白名單心態:只比對明確、具名的 App 簽章(上一節那份清單)。不要用「只要不是我認得的瀏覽器就當成 WebView」這種黑名單反向邏輯——那是誤判的溫床。
- iOS 特別小心:在 iOS 上,只比對具名 App(LINE、FB、IG 等)。絕對不要在 iOS 上用泛用的「WebView 啟發式判斷」,因為 iOS 版的 Chrome(
CriOS)、Firefox(FxiOS)底層其實都跑在系統的 WebView 元件上,泛用判斷會把這些真正的瀏覽器全部誤殺。 - 泛用
; wv)只用於 Android:那個泛用的 Android System WebView 標記,請限定只在 Android 上採用,且把它當成優先序最低的最後一道,能用具名簽章先命中就先命中。 - 寧可漏、不可錯:如果某個 case 你不確定,預設「放行」(當成正常瀏覽器、讓 OAuth 照常嘗試),而不是「攔截」。最壞情況不過是這個使用者看到 Google 的原生錯誤——和你沒做任何處理時一樣;但如果你誤攔了,反而製造了一個本來不存在的問題。
「改用外部瀏覽器開啟」手冊
偵測到使用者在 In-App Browser 之後,攔下 OAuth 按鈕,接著要給他一段清楚、可執行的引導。引導的目標只有一個:讓這個網址,在系統的真正瀏覽器裡重新打開一次。以下是我們驗證過、實際有效的幾種手段,建議組合使用:
1. 「複製連結」按鈕(最通用、跨平台保底)
提供一顆「複製此頁網址」的按鈕,一鍵把當前網址寫進剪貼簿,並附上一句話:「已複製連結,請打開 Chrome 或 Safari,貼上後前往,即可正常登入」。這是最不依賴特定 App、最不會出錯的保底方案——無論使用者在哪個 App、哪個作業系統,複製貼上總是行得通。
2. Android:用 intent:// 直接喚起 Chrome
在 Android 上,你可以用 Android Intent URL(intent:// 協定)直接要求系統用外部瀏覽器(通常是 Chrome)開啟目標網址。對 Android 使用者來說,這是體驗最順的一鍵跳出方案——點一下就直接跳到 Chrome,不必手動複製貼上。記得在 intent 裡帶上 fallback,萬一喚起失敗,仍退回到「複製連結」的提示。
3. 各 App 的選單提示(教使用者自己跳出)
iOS 上沒有像 Android intent 那樣可靠的程式化跳出方式,這時最務實的做法是用文字+圖示,教使用者操作該 App 自己的「在外部瀏覽器開啟」選項。常見路徑是按右上角的「⋯」或「≡」選單,找到「以其他瀏覽器開啟 / 在 Safari 中開啟 / Open in external browser」。建議針對不同 App 給出對應的提示文字(例如 LINE、FB、IG 的選單位置略有不同),讓引導更貼合使用者眼前真正看到的畫面。
這套組合的精神是:能一鍵就一鍵(Android intent),不能一鍵就一鍵複製(複製連結),都不行就手把手教(選單提示)。三層遞補,把「走出 In-App Browser」這件事的摩擦壓到最低。
登入卡關、轉換漏水,想要有人幫你把關?
從社群 In-App Browser 的登入封鎖,到整體註冊與信任流程的順暢度,這些「看不見的破口」往往最傷轉換。TrueLink 可以協助你檢視自家產品的登入與獲客動線,把放血的傷口補起來。
預約數位顧問諮詢 與我們聯絡別把 Email 密碼與 LINE 登入也擋掉
這是一個很容易踩到的反向錯誤:你好不容易做好了偵測,結果一偵測到 In-App Browser,就把所有登入方式全部封死,只留一句「請用外部瀏覽器開啟」。這就矯枉過正了——你又製造了新的摩擦。
關鍵原則是:只攔截「會被 WebView 封鎖」的那幾個登入方式,其餘能用的路一律保留暢通。具體來說:
- 只攔截 Google 與 Apple 的 OAuth 按鈕——因為只有它們會撞上
disallowed_useragent。 - 不要碰 Email/密碼登入。傳統的帳號密碼登入完全在你自己的網站內完成,不經過 Google 的 OAuth 封鎖,在 WebView 裡照樣能用。這是 In-App Browser 裡最可靠的一條路,務必保留。
- 不要碰 LINE 登入。LINE Login 走的是頂層頁面跳轉(top-level redirect),而不是嵌在 WebView 裡的 OAuth,因此它不受這個封鎖影響,在 LINE 的內建瀏覽器裡反而往往是最順的登入方式。把它擋掉就太可惜了。
所以理想的畫面是:在 In-App Browser 裡,使用者依然看得到「Email 登入」「LINE 登入」這些可用選項,只有 Google/Apple 那兩顆被換上了「請改用外部瀏覽器」的引導。永遠給使用者留一條走得通的路——這是把「攔截」做得不討人厭的底線。
無障礙:宣告引導、移動焦點
當你動態跳出一段引導訊息時,別忘了使用螢幕報讀器(screen reader)的使用者。對他們來說,一段「靜靜出現在畫面上」的文字是不存在的——除非你主動宣告它。把無障礙做好,本身也是數位信任的一環。要點:
- 用
role="status"宣告:把引導訊息容器標上role="status"(或適當的 live region),讓報讀器在訊息出現時自動朗讀出來,使用者才知道「剛剛發生了一件事、有新的指示」。 - 移動焦點(move focus):訊息出現後,把鍵盤焦點移到這段引導或其中的主要操作按鈕(例如「複製連結」),讓使用鍵盤或輔助科技的人能立刻接續操作,而不是被困在原本那顆已經失效的按鈕上。
- 尊重
prefers-reduced-motion:如果你的引導有任何動畫(淡入、滑入),務必偵測使用者的prefers-reduced-motion偏好設定,對選擇「減少動態效果」的人關閉或大幅簡化動畫,避免造成不適。
這些細節不會花你太多時間,但它們決定了你的「修復」是真的對所有人都友善,還是只對視力與操作正常的多數人友善。
為什麼這是看不見的轉換漏洞
講到這裡,你可能會想:「這真的有那麼嚴重嗎?」答案是:嚴重,而且嚴重得很安靜。
想想現在行動流量的來源結構。極大比例的行動使用者,並不是自己打開瀏覽器、輸入你的網址進來的;他們是在社群 App 裡看到別人分享的連結,順手一點進來的——而這一點,幾乎必然把他們丟進該 App 的內建瀏覽器。LINE 群組裡的轉發、Facebook 貼文的連結、IG 個人檔案的網址、朋友在 Messenger 傳的推薦——這些全都是 In-App Browser 流量。對許多以社群為主要獲客管道的中小企業與品牌來說,這甚至是最大宗的入口。
現在把兩件事疊在一起:你最重要的一批新訪客,從社群連結進來、落在 In-App Browser 裡;而他們最直覺、最常按的那顆「用 Google 登入」按鈕,在這個環境裡百分之百失敗。如果你沒有處理,會發生什麼?
- 使用者按下 Google 登入,看到嚇人的「存取遭封鎖」。
- 他不知道這是 WebView 的限制,只覺得「這網站怪怪的、不太能用」。
- 他不會去找「換瀏覽器」這種解法——他直接放棄、關掉、離開。
- 你這邊看到的,只是一個沒有完成註冊的 session。沒有錯誤回報、沒有客訴、沒有任何訊號告訴你「剛剛有一個準客戶在登入這一步無聲地流失了」。
這就是它最危險的地方:它是一個無聲的、不會出現在你錯誤日誌裡的、持續性的轉換流失。你的社群行銷做得越成功、導進來的 In-App Browser 流量越多,這個傷口流的血就越多——而你可能完全不知道它的存在。我們在 TrueLink 自家平台正是這樣後知後覺地發現它的:不是監控報警,而是一位願意開口的客戶隨口問了一句。有多少不開口的客戶,已經默默走了?
反過來說,這也意味著:把這個破口補起來,是少數「投入很小、回報直接」的轉換優化。你不需要重做整個登入系統,只需要加上一層偵測與引導。對的人在對的環境裡,順順地完成登入——順暢、可信賴的登入,正是數位信任最具體的一塊基石。
上線前檢查清單
最後,把整套做法收斂成一份可以直接拿去對照的清單:
- 偵測函式:抽成獨立、可單元測試的
isInAppBrowser(ua),比對 LINE/FB/IG/Messenger/WeChat/TikTok/KakaoTalk/Threads/Android 泛用 WebView 簽章。 - 保守判斷:白名單具名 App;iOS 只比對具名 App、不用泛用啟發式;泛用
; wv)只限 Android;不確定時預設放行。 - 攔截範圍:只攔 Google/Apple OAuth 按鈕,不碰 Email/密碼與 LINE 登入。
- 引導三層:Android
intent://一鍵喚起 Chrome → 「複製連結」保底 → 各 App「以外部瀏覽器開啟」選單文字提示。 - 無障礙:引導容器
role="status"、出現時移動焦點、尊重prefers-reduced-motion。 - 真機驗證:務必實際從 LINE/FB/IG 裡點連結進來測試——這是唯一能真正驗證的方式,桌面與一般行動瀏覽器都無法重現。
這個問題的本質,從來不是「Google 登入壞了」,而是「使用者被困在一個 Google 不信任的環境裡,而他自己不知道」。你的工作,就是當那個唯一看得清全局、並願意牽他一把的人。把它做好,你補的不只是一個登入按鈕,而是整個品牌在客戶第一哩路上的可信度。