Google App 內建瀏覽器:about:blank 佔位頁被關閉的排查紀錄
記錄 iOS Google App 內建瀏覽器在非同步開啟外部頁面時,about:blank 佔位分頁被自動關閉,導致第三方登入或外部導轉失敗的排查與解法。
W
這次遇到的問題,是在 iOS 的 Google App 內建瀏覽器中,第三方登入或外部導轉偶爾會完全沒有反應,最後只能等到流程逾時。
最後查到的原因不是 API,也不是 Google 登入本身,而是:
原本預先開好的
about:blank分頁,在等待伺服器回覆期間,被 Google App 自己關掉了。
為什麼需要先開一個空白分頁?
第三方登入或付款流程通常不是點下按鈕後,就立刻知道要前往哪個網址。
實際流程比較像:
使用者點擊按鈕
↓
向 Server 取得導轉網址
↓
等待回應
↓
開啟外部網站
但瀏覽器對 window.open() 有 Popup Blocker 限制。
如果等非同步請求完成後才呼叫:
const url = await getRedirectUrl();
window.open(url, "_blank");
新視窗可能已經失去使用者操作帶來的開窗權限,而被瀏覽器阻擋。
因此原本採用的是「先卡位、後導轉」:
const win = window.open("about:blank", "_blank");
const url = await getRedirectUrl();
win.location.href = url;
流程變成:
使用者點擊
↓
先開 about:blank
↓
等待 Server
↓
取得網址
↓
把剛才的分頁導向真正網址
這個做法在一般 Safari、Chrome 上原本都能正常使用。
Google App 的問題:about:blank 自己消失了
問題出現在 Google App 的 iOS 內建瀏覽器。
實測發現:
window.open("about:blank")
↓
剛開啟時 window.closed = false
↓
等待約 1 秒
↓
window.closed = true
也就是:
分頁確實成功開啟了,但還沒等 Server 回傳網址,就被容器關掉了。
後續即使再執行:
win.location.href = redirectUrl;
也已經沒有作用,因為 win 指向的視窗早就被關閉。
換成真正的佔位頁後就正常
為了確認問題是不是「跨網域導轉」造成的,當時做了幾組對照測試:
| 測試方式 | 結果 |
|---|---|
| 直接開啟真正網址 | ✅ 正常 |
about:blank → 延遲後導轉 | ❌ 失敗 |
| 同源佔位頁 → 延遲後導轉 | ✅ 正常 |
| 同源佔位頁 → Google | ✅ 正常 |
| 同源佔位頁 → 其他外部網站 | ✅ 正常 |
最關鍵的差異就是:
about:blank
→ 會被關閉
而:
/redirecting.html
→ 分頁持續存在
→ 可以正常導向下一個網址
因此最後沒有再使用純空白頁,而是改成一個非常簡單的同源佔位頁:
<h1>處理中</h1>
<p>請稍候...</p>
不需要複雜 JavaScript,只要它是一個真正載入完成的頁面即可。
只針對 Google App 分流
因為 Safari、Chrome 原本的流程沒有問題,所以最後沒有全面修改。
而是透過 User-Agent 中的 GSA/ 特徵辨識這次遇到的 Google App 環境:
function isGoogleApp() {
return /\bGSA\//.test(navigator.userAgent);
}
再決定預開哪一種頁面:
const placeholderUrl = isGoogleApp()
? "/redirecting.html"
: "about:blank";
const win = window.open(placeholderUrl, "_blank");
概念就是:
Google App
└─ 開真正的同源佔位頁
Safari / Chrome
└─ 維持原本 about:blank
這樣可以把修改範圍控制在實際發生問題的環境,不影響原本正常的瀏覽器。
這次踩坑後的心得
這次最容易誤判的地方,是:
window.open() 有成功
不代表:
這個 Window 一段時間後還活著
如果流程是:
先開視窗
↓
等待非同步結果
↓
再修改 location
除了要檢查:
win !== null
其實還可以注意:
win.closed
尤其是在 App 內建瀏覽器或 WebView 環境中,行為不一定和 Safari、Chrome 完全相同。
最後我的處理原則變得很簡單:
如果必須預開分頁等待非同步結果,不要假設
about:blank在所有瀏覽器容器裡都會一直存在;必要時使用真正的同源佔位頁。