自訂 Captive Portal:HTML 與 CSS 指南
本權威技術參考指南概述了設計和編寫自訂 Captive Portal 登入頁面所需的開發標準、CSS 架構和網路層級限制。它為前端開發人員和網路架構師提供了實用的策略,以應對 Apple CNA 和 Android webview 環境,確保提供像素級完美、合規且高效能的顧客 WiFi 體驗。
收聽此指南
查看播客逐字稿
📚 核心系列的一部分:Captive Portal 終極指南 →
- 執行摘要
- 技術深度剖析
- Captive Portal 生命週期
- 平台專屬的微型瀏覽器限制
- 繞過 Apple CNA「完成」按鈕陷阱的編碼方法
- 實作指南
- 黃金法則:針對零網際網路連線進行設計
- 1. 檢視區配置
- 2. 內嵌 CSS 並移除外部相依性
- 3. Base64 資產編碼
- 4. 輸入欄位與點擊目標最佳化
- 5. 法律同意與 GDPR 合規性
- 自訂 Captive Portal HTML/CSS 範本
- 疑難排解與風險規避
- 1. SSL/TLS 憑證警告無限循環
- 2. DNS 解析失敗(圍牆花園陷阱)
- 3. 工作階段逾時與 MAC 偽造漏洞
- Purple 產品關聯性:自行建置 vs. 直接購買
- 投資報酬率與商業影響力
- 1. 降低營運成本 (IT 客服中心支援工單)
- 2. 行銷數據擷取與訂閱同意最佳化
- 3. 法律與法規風險規避
- 重點摘要
- 參考文獻
- 收聽技術簡報

執行摘要
對於企業級場域 - 從奢華酒店 Hospitality 與零售連鎖店 Retail 到交通樞紐 Transport 以及現代醫療園區 Healthcare - 顧客 WiFi 登入頁面就是數位化大門。然而,超過 90% 的顧客 WiFi 登入發生在行動裝置上,其渲染並非由 Safari 或 Chrome 等標準瀏覽器控制,而是受到高度限制的 Captive Network Assistant (CNA) 網頁檢視 (webview) 所約束 [1]。這些「微型瀏覽器」強制執行嚴格的沙箱限制:它們會阻擋外部 CDN、停用持續性 Cookie、忽略外部網頁字型,並嚴格限制 JavaScript 執行,以降低安全風險並防止工作階段綁架 [2]。
當開發人員使用傳統網頁標準設計登入頁面時,這些限制會導致排版損毀、品牌資產遺失以及登入按鈕失效,直接影響客戶滿意度和數位互動。本指南提供了應對這些挑戰的解決方案,介紹了防禦性編碼實踐 - 例如行內 CSS、Base64 資產編碼、系統字型堆疊以及明確導向導航的驗證交握 - 以確保無縫的跨平台渲染。此外,我們還探討了如何利用像 Purple 的 portal builder 這類託管解決方案,讓開發人員在保持完整 HTML/CSS 創意控制的同時,將 RADIUS 驗證、資料庫擴充、GDPR/PCI 合規性以及多供應商 AP 整合等工作託管出去 [3]。
技術深度剖析
要構建一個具備彈性的自訂 Captive Portal,開發人員必須了解當顧客關聯到開放式 SSID 時所發生的網路層攔截和瀏覽器虛擬化。
Captive Portal 生命週期
當用戶端裝置與 captive SSID 關聯時,會觸發以下程序:
- IP 關聯:裝置完成三向交握,並透過 DHCP 請求 IP 位址。
- 主動連線探測:作業系統的背景網路管理員會立即向專用的供應商中立 Canary URL(例如 Apple 的
http://captive.apple.com/hotspot-detect.html或 Google 的http://connectivitycheck.gstatic.com/generate_204)發送 HTTP GET 請求 [1]。 - DNS/HTTP 攔截:本機無線區域網路控制器 (WLC) 或 Access Point (AP) 會攔截此 port 80 HTTP 請求。閘道器不會傳回預期的 HTTP 200 或 204 狀態,而是透過 HTTP 302 重新導向將用戶端的流量重導向至 captive portal 的登陸頁面 URL [2]。
- 產生網頁檢視:偵測到重新導向後,作業系統會產生其內建的 Captive Network Assistant (CNA) 微型瀏覽器來顯示重新導向的登入頁面,使用者無需手動開啟完整瀏覽器。
- 驗證與狀態轉移:使用者填寫登入表單,將憑證提交回傳送入口伺服器,伺服器會指示閘道器(通常透過 RADIUS Access-Accept 或外部 API 呼叫)授權該 MAC 位址。
- CNA 結束握手:CNA 微型瀏覽器對其 Canary URL 執行另一個 HTTP GET。如果收到預期的 200/204 回應,它會將其右上角的按鈕從「取消」變更為「完成」,並將 WiFi 連線建立為主要網路介面。
平台專屬的微型瀏覽器限制
每個作業系統都在不同的 webview 環境中處理此生命週期,從而導致高度破碎的行為。下表詳細列出了這些關鍵限制:
| 平台 / Webview | 顯示方式 | 永久性 Cookie | 外部網頁字型 | JavaScript 執行 | 視窗尺寸 | 結束握手觸發器 |
|---|---|---|---|---|---|---|
| Apple iOS CNA (Websheet) | 微型瀏覽器彈出視窗 | 已封鎖(關閉時銷毀) | 已封鎖(離線) | 受限(無 localStorage/sessionStorage) | 自適應(裝置寬度) | 僅限全頁 HTTP 重新導向 [1] |
| Apple macOS CNA (Captive Network Assistant) | 微型瀏覽器彈出視窗 | 已封鎖 | 已封鎖 | 受限(無 alert/confirm 對話框) | 固定 (900px x 572px) | 僅限全頁 HTTP 重新導向 |
| Android (Google) (CaptivePortalLogin) | 推播通知 -> Chrome Custom Tab | 允許(與 Chrome 共用) | 允許(若在圍牆花園白名單中) | 完整 | 自適應 | 自動 (Captive Portal API / 204 檢查) [2] |
| Samsung Android (Samsung Internet) | 推播通知 -> 微型瀏覽器 | 允許 | 允許 | 完整 | 自適應 | 自動 |
| Windows 10/11 (預設瀏覽器) | 自動啟動預設瀏覽器 | 允許(完整瀏覽器環境) | 允許 | 完整 | 自適應 | 手動 / 自動 |

繞過 Apple CNA「完成」按鈕陷阱的編碼方法
自訂傳送入口開發中最常見的失敗模式之一是 iOS 裝置上的**「完成」按鈕陷阱**。當使用者進行驗證時,iOS Websheet webview 必須偵測到網路不再處於受限狀態。它透過監控其背景 Canary 請求的成功與否來做到這一點。
至關重要的是,iOS CNA 僅會在全頁面 HTTP 導向(位置重導向)時觸發此檢查。如果開發人員建置了一個現代單頁應用程式(SPA),並透過非同步 AJAX 呼叫(例如 fetch() 或 Axios)提交表單資料,並在不變更 URL 的情況下動態更新 DOM,CNA 將 永遠不會 重新執行其連線檢查。使用者將在閘道層級通過驗證,但右上方角的 CNA 按鈕仍將保持為「取消」。如果感到挫折的使用者點擊「取消」,iOS 裝置將立即與 SSID 中斷關聯,終止 WiFi 工作階段 [1]。
為防止這種情況,驗證成功處理常式 必須 執行全頁面重導向至實體落地網頁(例如 window.location.href = '/success'),或透過標準 HTTP POST 動作以原生方式提交登入表單。
實作指南
為確保在所有平台上的呈現一致,開發人員必須從現代、資源密集的網頁設計,轉變為高度自包含、防禦性的編碼風格。
黃金法則:針對零網際網路連線進行設計
在 Captive Portal 狀態下,用戶端裝置無法存取更廣泛的網際網路。它只能解析和存取無線控制器 Walled Garden 中明確列入白名單的 IP 位址和網域(例如 Captive Portal 伺服器本身的 IP)。因此,HTML 中引用的任何外部資源都將無法載入,從而導致版面配置損壞。
為了進行防禦性設計,請實作以下 行動優先 Captive Portal 設計檢查清單:

1. 檢視區配置
為了防止行動裝置將檢視區縮小至桌上型電腦寬度(通常為 980px),HTML <head> 必須包含一個響應式檢視區 meta 標籤。如果沒有此標籤,文字和輸入欄位在行動裝置上會顯得微乎其微:
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
2. 內嵌 CSS 並移除外部相依性
切勿連結到外部 CSS 檔案或 CDN(例如 Bootstrap、Tailwind 或 Google Fonts)。所有 CSS 都必須嵌入 HTML <head> 內的 <style> 區塊中。
對於字型,請使用引用預先安裝的本機作業系統字型的系統字型堆疊。這完全避免了 HTTP 請求,並確保使用裝置的原生、高品質無襯線字型立即載入網頁:
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
background-color: #F5F1ED; /* Pearl White 品牌色彩 */
color: #011638; /* Dark Blue 品牌色彩 */
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 100vh;
}
3. Base64 資產編碼
除非代管網域已被列入閘道器的圍牆花園(Walled Garden)白名單,否則不應透過外部 HTTP URL 參照圖片、標誌(Logo)和圖示。最可靠的方法是將小型視覺資產(例如 PNG 或 SVG 標誌)直接編碼為 Base64 資料 URI,嵌入至 HTML 或 CSS 中:
<!-- 行內 Base64 標誌範例 -->
<img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMDAgMTAwIj48Y2lyY2xlIGN4PSI1MCIgY3k9IjUwIiByPSI0MCIgZmlsbD0iIzc0NThGRCIvPjwvc3ZnPg==" alt="Purple Logo" class="brand-logo">
4. 輸入欄位與點擊目標最佳化
為符合行動裝置可用性標準(例如 Apple 的 Human Interface Guidelines),所有互動式元素都必須針對觸控輸入進行最佳化:
- 輸入欄位字型大小:輸入文字字型大小必須至少為 16px。如果字型大小較小(例如 14px),iOS Safari 和 CNA 在聚焦該輸入欄位時會自動放大,導致頁面排版變形 [1]。
- 點擊目標:按鈕、核取方塊和連結的最小觸控目標區域必須為 44px x 44px,以防止誤觸或造成使用者困擾。
5. 法律同意與 GDPR 合規性
擷取訪客資料(例如電子郵件、電話號碼或社群設定檔)時,HTML 結構必須支援明確的法律同意。在 GDPR 規範下,預先勾選的核取方塊或隱含的同意聲明(例如「登入即代表您同意我們的條款」)皆不合規 [3]。
- 為服務條款和隱私權政策提供一個預設為未勾選的核取方塊。
- 將詳細的法律條文封裝在一個可捲動、限制高度的容器中(
max-height: 120px; overflow-y: auto;),以確保主要的登入表單保持在首頁上半部(Above the Fold)。
<div class="consent-container">
<input type="checkbox" id="terms_consent" name="terms_consent" required>
<label for="terms_consent">
我明確同意 <a href="#" onclick="showTerms()">服務條款</a>,並同意依據 <a href="#" onclick="showPrivacy()">隱私權政策</a> 處理我的資料。
</label>
</div>
自訂 Captive Portal HTML/CSS 範本
以下是一個完整、可用於生產環境的單一檔案 HTML/CSS 範本,融合了所有行動裝置優先、符合 CNA 規範的最佳實踐。它具備響應式排版、系統字型堆疊、Base64 專用資產預留位置、觸控友好的輸入欄位,以及符合 GDPR 規範的明確同意結構:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>Guest WiFi Login</title>
<style>
/* CSS Reset & Variable Declarations */
:root {
--color-primary: #7458FD; /* Purple Brand Colour */
--color-dark: #011638; /* Dark Blue */
--color-light: #F5F1ED; /* Pearl White */
--color-white: #FFFFFF;
--color-border: #D1D5DB;
--radius-container: 12px;
--radius-element: 6px;
}
* {
box-sizing: border-box;
-webkit-tap-highlight-color: transparent;
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
background-color: var(--color-light);
color: var(--color-dark);
margin: 0;
padding: 20px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 100vh;
}
/* 響應式卡片版面配置 */
.portal-card {
background-color: var(--color-white);
width: 100%;
max-width: 420px;
padding: 32px 24px;
border-radius: var(--radius-container);
box-shadow: 0 4px 20px rgba(1, 22, 56, 0.05);
text-align: center;
}
.logo-container {
margin-bottom: 24px;
}
/* 內嵌 Base64 Logo 預留位置 */
.logo {
width: 120px;
height: auto;
}
h1 {
font-size: 24px;
font-weight: 700;
margin: 0 0 8px 0;
color: var(--color-dark);
}
p {
font-size: 14px;
color: #4B5563;
margin: 0 0 24px 0;
line-height: 1.5;
}
/* 表單樣式 */
.form-group {
text-align: left;
margin-bottom: 16px;
}
label {
display: block;
font-size: 14px;
font-weight: 600;
margin-bottom: 6px;
color: var(--color-dark);
}
/* 重要:字型大小必須至少為 16px,以防止 iOS 自動縮放 */
input[type="email"],
input[type="text"] {
width: 100%;
height: 48px;
padding: 12px 16px;
font-size: 16px;
border: 1px solid var(--color-border);
border-radius: var(--radius-element);
background-color: var(--color-white);
color: var(--color-dark);
outline: none;
transition: border-color 0.2s;
}
input[type="email"]:focus,
input[type="text"]:focus {
border-color: var(--color-primary);
box-shadow: 0 0 0 3px rgba(116, 88, 253, 0.15);
}
/* 同意聲明樣式 */
.consent-group {
display: flex;
align-items: flex-start;
text-align: left;
margin: 20px 0;
}
/* 適合觸控操作的目標尺寸 */
input[type="checkbox"] {
width: 20px;
height: 20px;
margin: 2px 12px 0 0;
flex-shrink: 0;
cursor: pointer;
accent-color: var(--color-primary);
}
.consent-label {
font-size: 13px;
line-height: 1.4;
color: #4B5563;
user-select: none;
}
.consent-label a {
color: var(--color-primary);
text-decoration: none;
font-weight: 600;
}
.consent-label a:hover {
text-decoration: underline;
}
/* Touch-Friendly CTA Button (Minimum 44px height) */
.btn-submit {
width: 100%;
height: 48px;
background-color: var(--color-primary);
color: var(--color-white);
border: none;
border-radius: var(--radius-element);
font-size: 16px;
font-weight: 600;
cursor: pointer;
transition: background-color 0.2s;
display: flex;
align-items: center;
justify-content: center;
}
.btn-submit:hover {
background-color: #5B3EE3;
}
.btn-submit:active {
background-color: #4A2FD4;
}
/* Legal Terms Scrollbox */
.terms-scrollbox {
display: none; /* Toggled via JS */
text-align: left;
font-size: 11px;
color: #6B7280;
background-color: var(--color-light);
padding: 12px;
border-radius: var(--radius-element);
max-height: 100px;
overflow-y: auto;
margin-bottom: 16px;
border: 1px solid var(--color-border);
}
.footer {
margin-top: 24px;
font-size: 11px;
color: #9CA3AF;
}
</style>
</head>
<body>
<div class="portal-card">
<div class="logo-container">
<!-- Replace src with your actual Base64-encoded SVG or PNG -->
<svg class="logo" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 30">
<rect width="100" height="30" rx="6" fill="#7458FD"/>
<text x="50" y="20" font-family="sans-serif" font-size="12" fill="#FFFFFF" font-weight="bold" text-anchor="middle">您的品牌</text>
</svg>
</div>
<h1>歡迎使用訪客 WiFi</h1>
<p>請在下方輸入您的詳細資訊以取得安全的快速度網路連線。</p>
<!-- Form must submit natively (full page navigation) to clear Apple CNA -->
<form action="/login/submit" method="POST" onsubmit="return validateForm()">
<div class="form-group">
<label for="guest_name">姓名</label>
<input type="text" id="guest_name" name="name" placeholder="王小明" required autocomplete="name">
</div>
<div class="form-group">
<label for="guest_email">電子郵件地址</label>
<input type="email" id="guest_email" name="email" placeholder="john@example.com" required autocomplete="email">
</div>
<div class="consent-group">
<input type="checkbox" id="terms_consent" name="terms_accepted" required>
<label for="terms_consent" class="consent-label">
我接受 <a href="#" onclick="toggleTerms(event)">服務條款</a> 並同意依照 GDPR 規範進行數據處理。
</label>
</div>
<div id="terms_box" class="terms-scrollbox">
<strong>WiFi 服務條款:</strong><br>
1. 本服務按「現況」提供,不提供任何保證。<br>
2. 使用者不得從事非法或佔用高頻寬的活動。<br>
3. 個人數據的收集僅用於身分驗證與行銷訂閱,並符合我們的隱私權政策。<n>
</div>
<button type="submit" class="btn-submit">連接至 WiFi</button>
</form>
<div class="footer">
Powered by Purple | 安全訪客 WiFi
</div>
</div>
<script>
function toggleTerms(e) {
e.preventDefault();
var box = document.getElementById('terms_box');
box.style.display = (box.style.display === 'block') ? 'none' : 'block';
}
function validateForm() {
var consent = document.getElementById('terms_consent');
if (!consent.checked) {
alert('您必須接受服務條款才能進行連接。');
return false;
}
return true;
}
</script>
</body>
</html>
疑難排解與風險規避
在部署自訂程式碼的 HTML/CSS Captive Portal 時,IT 運維團隊經常會遇到幾個嚴重的營運風險:
1. SSL/TLS 憑證警告無限循環
由於 Captive Portal 是透過攔截流量來運作,這與現代 HTTPS 網路安全機制存在根本上的衝突。當使用者嘗試造訪 HTTPS 網站(例如 https://www.google.com),而閘道器企圖將該流量重新導向至 HTTP Captive Portal 時,瀏覽器會偵測到 SSL 憑證不符合,並顯示嚴重的「您的連線不是安全連線」安全警告。
- 規避措施:切勿嘗試直接攔截 HTTPS 流量。應完全依賴作業系統內建的 CNA 輔助程式(該程式會發送未加密的 HTTP 請求以觸發重新導向)。確保您的 Captive Portal 網域擁有有效且受公開信任的 SSL 憑證(例如 Let's Encrypt 或 DigiCert),且僅在初始 HTTP 重新導向成功將使用者路由至您的入口網站網域後,才透過 HTTPS 提供服務 [2]。
2. DNS 解析失敗(圍牆花園陷阱)
如果您的自訂 HTML 頁面引用了外部資源(例如 Facebook、Google 等社群登入 OAuth 端點,或付款閘道),除非將這些網域明確列入無線控制器的 Walled Garden 白名單中,否則針對這些網域的 DNS 請求將會失敗。如果白名單中遺漏了某個網域,登入流程將會停滯並顯示空白畫面。
- 因應對策:維護一份嚴格且精簡的 Walled Garden 清單。如果使用社群登入,請將身分驗證提供者建議的特定萬用字元網域列入白名單(例如
*.google.com、*.gstatic.com)。
3. 工作階段逾時與 MAC 偽造漏洞
標準的 Captive Portal 是根據裝置的 MAC 位址進行驗證。然而,現代行動作業系統(iOS 14+ 和 Android 10+)預設使用隨機 MAC 位址(私用 WiFi 位址),並會定期輪替。這可能會導致訪客重複收到重新驗證的提示,進而破壞使用者體驗 [1]。
- 因應對策:在 RADIUS 伺服器上實施合理的工作階段逾時設定(例如 24 小時)以防止過期工作階段,並採用 Passpoint (Hotspot 2.0) 或 WPA3-Enterprise 等現代驗證標準,實現完全繞過基於 MAC 的 Captive Portal 的無縫、安全註冊流程。
Purple 產品關聯性:自行建置 vs. 直接購買
雖然編寫單個 HTML 頁面非常簡單,但託管、保護和擴充自訂的 Captive Portal 基礎架構會面臨巨大的技術和合規性障礙。下表比較了自行託管自訂入口網站與使用 Purple 的託管企業平台在工程與營運上的現實差異:
| 功能 / 營運需求 | 自行託管的自訂入口網站 | Purple 企業級 WiFi 平台 |
|---|---|---|
| HTML/CSS 自訂 | 完全手動編寫程式碼,並將檔案上傳到個別 AP 或本機網頁伺服器。 | 像素級精準的開發人員編輯器,允許自訂 HTML/CSS 嵌入,並結合拖放式視覺化建置工具。 |
| RADIUS 基礎架構 | 必須部署、設定並維護高可用性的 FreeRADIUS 或雲端 RADIUS 伺服器 [4]。 | 內建、全球分佈、雲端原生 RADIUS,提供雙活(Active-Active)備援和 99.99% 的可用性 SLA。 |
| 多廠商 AP 支援 | 每個硬體廠商(Cisco、Aruba、Meraki、Ruckus)都需要自訂整合指令碼 [5]。 | 與 200 多種硬體型號進行原生、即開即用的整合;在混合硬體的資產中實現統一的入口網站部署。 |
| 資料隱私與合規性 | 場所需承擔 100% 的 GDPR、CCPA 和 PCI-DSS 合規性法律責任,包括安全的資料庫加密和資料刪除工作流程。 | 設計上完全合規。內建同意聲明管理、自動化資料主體刪除請求,以及安全的 ISO 27001 認證託管。 |
| Identity Provider Integrations | Manual OAuth2 integrations with Google, Facebook, Apple, and local SMS gateways. | One-click integrations with major social platforms, SMS gateways, and Azure AD / Okta for corporate guests. |
Purple 的平台解決了「自行建置還是外購」的兩難。它為開發人員提供了自訂 HTML/CSS 工作區的完整創意自由,同時消除了大規模支援安全 RADIUS 驗證所需、複雜且高風險的後端基礎架構工程。
投資報酬率與商業影響力
投資專業設計、具備自適應特性的自訂 Captive Portal,可在 IT 營運、行銷和法規遵循方面帶來可量化的回收。
1. 降低營運成本 (IT 客服中心支援工單)
在體育場或多據點零售連鎖店等大規模部署中,故障的 Captive Portal 是導致 IT 客服中心升級工單的主要原因。當訪客遇到「白畫面」或無法回應的登入按鈕時,他們會讓現場工作人員應接不暇,或提交支援工單。
$$\text{年度支援省下成本} = (\text{年度訪客總造訪次數} \times \text{Portal 失敗率} \times \text{客服中心聯繫率}) \times \text{每張支援工單成本}$$
- 情境:一個每年有 1,000,000 名訪客的會議中心。一個程式碼編寫不良的 Portal 在舊版 iOS 裝置上的失敗率為 5%,導致 10% 的客服中心聯繫率。以業界標準每張支援工單 15 美元計算,營運成本為: $$(1,000,000 \times 0.05 \times 0.10) \times $15 = $75,000 \text{ 每年可避免的支援開銷}$$
- 結果:轉換為針對 CNA 最佳化、行動優先的範本,可將 Portal 失敗率降低至 <0.1%,幾乎消除了這項營運損耗。
2. 行銷數據擷取與訂閱同意最佳化
對於零售和旅宿場所,訪客 WiFi Portal 是擷取乾淨的第一方顧客數據的主要機制。使用者介面如果設計不良,字型過小或表單版面配置笨重,會導致高跳出率 - 使用者會完全放棄登入程序,從而失去行銷機會。
- 案例研究 (零售):某家全國零售連鎖店採用了利用 Purple 平台建置的行動優先最佳化 Captive Portal。透過將多步驟登入表單替換為單一欄位電子郵件輸入 (字型大小:16px) 以及最佳化的 48px 點擊目標按鈕,他們在第一季內實現了完成註冊量增加 42% 以及行銷電子報訂閱同意率增加 28% 的成果 [6]。
3. 法律與法規風險規避
在 GDPR 與 CCPA 的規範下,不合規的數據收集會帶來嚴厲的財務處罰(在 GDPR 下最高可達全球年營業額的 4%)。依賴預先勾選的核取方塊,或未能於登入頁面提供清晰且易於存取的隱私權政策,會使企業面臨巨大的法律責任。
- 降低風險的投資報酬率:實施明確且預設未勾選的同意核取方塊,並在最佳化的捲動文字方塊中託管條款,可確保 100% 符合法規,從而降低數百萬美元法規罰款的風險,並保護品牌聲譽。
重點摘要
- CNA 沙箱具限制性:Apple 的 iOS Websheet 與 macOS CNA 是高度沙箱化的環境,會封鎖外部資源、Cookie 與網頁字型。所有樣式與資源必須自包含(行內 CSS、Base64 圖片、系統字型) [1]。
- AJAX 會破壞 iOS 結束握手:若要成功將 iOS 裝置從「受限連線」轉換為「已連線」(將右上角按鈕從「取消」變更為「完成」),您必須觸發全頁面 HTTP 重新導向。非同步 DOM 更新會使裝置陷入受限連線的無限循環中。
- 行動優先為強制要求:超過 90% 的登入發生在行動裝置上。請設計單欄版面配置(最大寬度:480px)、使用適合觸控的點選目標(至少 44px x 44px),並對所有文字輸入欄位強制執行至少 16px 的字型大小,以防止 iOS 瀏覽器自動縮放。
- 圍牆花園(Walled Gardens)控制 DNS:登入過程中參照的任何外部網域(例如社群登入 API)都必須在無線控制器的圍牆花園中明確列入白名單,否則頁面將無法載入。
- Purple 消除後端複雜性:使用 Purple 的入口網站建置工具,開發人員可透過自訂編輯器獲得完整的 HTML/CSS 控制權,同時卸下 RADIUS、多供應商 AP 整合以及符合 GDPR 的資料庫管理等在安全性、擴充性與合規性方面的巨大負擔 [3]。
參考文獻
- [1] Wireless Broadband Alliance: Captive Network Portal Behaviour
- [2] Android Open Source Project: Captive Portal Login Webview Integration
- [3] European Data Protection Board: Guidelines on Consent under Regulation 2016/679
- [4] 如何使用雲端 RADIUS 實作 802.1X 驗證
- [5] Cisco 無線 AP:2026 年產品與部署指南
- [6] Purple WiFi 行銷與分析平台
收聽技術簡報
收聽資深方案架構師討論自訂 Captive Portal 的技術限制與部署策略:
關鍵定義
Captive Portal
在向新連接的 WiFi 網路使用者授予更廣泛的網路資源存取權限之前,向其顯示的網頁,通常用於驗證、付款或顯示服務條款。
IT 團隊在閘道層級部署 Captive Portal,以控制訪客存取、收集使用者資料並執行法律合規性。
Captive Network Assistant (CNA)
由作業系統(例如 Apple iOS 和 macOS)在偵測到 Captive 網路重新導向時自動產生的極度受限、沙盒化的微型瀏覽器,其目的僅在於促進入口網站驗證。
CNA 網頁檢視執行嚴格的限制,包括封鎖外部 CDN、持續性 Cookie 和本機儲存,這經常會破壞標準的網頁設計。
Walled Garden
一個受限制的 IP 位址、子網路或網域名稱清單,未經驗證的訪客使用者在完成 Captive Portal 登入程序之前,被允許透過閘道存取這些內容。
開發人員必須確保任何外部資源(例如社群登入 API 或付款閘道)都已列入 Walled Garden 的白名單,以防止登入流程停滯。
Base64 Encoding
一種將二進位資料(如影像)表示為 ASCII 字串的二進位轉文字編碼配置,允許將資產直接內嵌在 HTML 或 CSS 文件中。
利用 Base64 Encoding 處理標誌和圖示可以消除外部 HTTP 請求,確保資產在離線 CNA 環境中完美呈現。
RADIUS (Remote Authentication Dial-In User Service)
一種網路協定,為連接和使用網路服務的使用者提供集中式的驗證、授權和計帳(AAA)管理。
一旦滿足驗證條件,Captive Portal 伺服器就會與 RADIUS 伺服器通訊,在網路閘道端對訪客的 MAC 位址進行授權。
System Font Stack
一種 CSS font-family 宣告,優先使用預先安裝的作業系統字型(例如 iOS 上的 San Francisco、Windows 上的 Segoe UI 以及 Android 上的 Roboto),而非外部網路字型。
實作 System Font Stack 可確保立即轉譯排版,而不會觸發針對 Google Fonts 等服務被封鎖的外部 HTTP 請求。
Canary URL
由作業系統廠商維護的專用、未加密 HTTP URL(例如 captive.apple.com),用於測試裝置是否擁有不受限制的網際網路連線能力。
作業系統背景網路管理器會檢查此 URL,以偵測是否存在 Captive Portal 並觸發 CNA 網頁檢視彈出視窗。
Passpoint (Hotspot 2.0)
由 Wi-Fi Alliance 開發的產業標準,使行動裝置能夠自動發現 Wi-Fi 熱點並進行安全驗證,跳過手動的 Captive Portal 登入流程。
企業將 Passpoint 與 Purple 等平台結合使用,讓訪客從繁瑣的歡迎頁面過渡到無縫、類似行動網路的安全漫遊體驗。
範例
一家擁有 250 間客房的奢華連鎖飯店 [飯店餐旅業](/industries/hospitality) 希望實作一個完全符合其頂級品牌形象的自訂顧客 WiFi 登入頁面。他們的廣告代理商設計了一個啟動頁面,使用自訂的品牌字型(託管於 Adobe Fonts)、多個高解析度背景圖片(託管於公開的 AWS S3 儲存桶)以及多步驟的 JavaScript 動效精靈。部署後,iOS 顧客連接到 SSID,但彈出的 Portal 頁面卻是一片空白,且使用者無法進行驗證。
要解決空白畫面和品牌破圖的問題,我們必須重構 Portal 的前端架構,以符合 Apple CNA 沙盒限制:
- 字型修復:由於 Adobe Fonts 需要外部 HTTP 請求,而該請求會被 CNA 阻擋,我們將自訂字型呼叫替換為原生、優質的系統字型集(
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;)。這可確保無需外部網路呼叫即可立即渲染。 - 資產最佳化:AWS S3 上的背景圖片被阻擋,因為 S3 不在閘道器的圍牆花園(Walled Garden)中。我們壓縮主要品牌標誌,將其轉換為輕量級的 SVG,並將其作為 Base64 Data URI 直接編碼在 HTML 中。對於背景,我們使用飯店品牌色彩的簡潔、響應式 CSS 漸層取代沉重的圖片,從而大幅減輕頁面大小。
- JavaScript 簡化:多步驟動效精靈依賴外部 jQuery 和 GSAP 程式庫。我們去除這些外部依賴關係,並將表單重構為單頁、單欄的 HTML 結構。表單驗證則使用輕量級的原生 JavaScript 重寫。
- 驗證交握:表單提交從基於 AJAX 的提交修改為原生的 HTML
<form action="/submit" method="POST">,以觸發全頁面重導向,使 iOS Websheet 能夠執行其 Canary 測試並顯示「完成」按鈕。
一家擁有 450 家門市的連鎖零售商 [零售業](/industries/retail) 希望透過 WiFi 啟動頁面收集顧客電子郵件,以充實其 CRM。他們要求顧客選擇訂閱行銷電子報。初始設計包含一個預先勾選的「我同意接收行銷電子郵件」核取方塊。此外,該 Portal 託管在總部的單一本地伺服器上。在尖峰時段(週六下午),全國各地的顧客都會遇到嚴重的延遲,許多人無法載入登入頁面,導致數據收集率大幅下降。
我們必須同時解決合規性違規和基礎架構瓶頸問題:
- 合規性補救措施:在 GDPR 和 CCPA 的規定下,預先勾選的同意方塊是非法的。我們修改 HTML,將行銷同意核取方塊預設設為未勾選(
<input type="checkbox" id="marketing_consent">)。我們還為服務條款添加了一個獨立的、強制性的核取方塊,以將法律協議與行銷訂閱脫鉤。 - 基礎架構擴充:將全國性的 Captive Portal 託管在單一集中式伺服器上會建立單一故障點,並造成巨大的延遲瓶頸。我們將入口網站前端遷移到具有邊緣快取、高可用性且全球分佈的內容傳遞網路(CDN)。
- RADIUS 整合:我們設定本地商店的存取點,指向具有主動 - 主動備援的雲端原生 RADIUS 叢集,確保即使在週六的流量尖峰期間,驗證請求也能在邊緣端以低於 50 毫秒的延遲進行本地處理。
- Purple 遷移:為了消除這整個工程開銷,零售商遷移到了 Purple。Purple 內建的 GDPR 同意工具會自動管理符合合規要求的訂閱,其全球分佈的雲端基礎架構能以 99.99% 的上線時間處理每日數百萬次的驗證,徹底解決了擴充瓶頸。
練習題
Q1. 一家大型國際機場 [Transport](/industries/transport) 的 IT 團隊部署了自訂編碼的 Captive Portal。他們發現,雖然 Android 使用者可以無縫連線,但很大一部分的 iOS 使用者遇到了成功驗證卻無法瀏覽網頁的問題。仔細觀察後,這些 iOS 裝置顯示已連線到 SSID,但強制登入彈出視窗右上角的按鈕仍顯示「取消」而非「完成」。此問題的根本原因為何?開發人員應如何修正?
提示:分析 Apple CNA 小幫手如何偵測網路已從受限狀態轉變為已驗證狀態,以及需要何種瀏覽器動作來觸發此檢查。
查看標準答案
根本原因是該入口網站的成功頁面是透過 JavaScript(AJAX/SPA 路由)動態更新 UI,而不是執行整頁的 HTTP 導航。Apple iOS Captive Network Assistant (CNA) 迷你瀏覽器只有在發生整頁 URL 重新導向或導航時,才會重新執行其背景連線檢查(對 captive.apple.com 的 canary 請求)。如果開發人員透過 AJAX 送出登入表單並僅在 DOM 中顯示「成功」訊息,CNA 將無法得知網路已被解除鎖定。因此,右上角的按鈕仍保持為「取消」。如果使用者按「取消」結束,作業系統會假設登入失敗並中斷與 WiFi 網路的連線。
解決方案:開發人員必須修改驗證成功處理常式以強制進行整頁重新導向。這可以透過使用標準 HTML <form action="/submit" method="POST"> 原生送出登入表單來實現,或者在 API 傳回成功的驗證回應後,在 JavaScript 中執行 window.location.href = '/success_landing_page'。這會觸發所需的整頁載入,強制 CNA 小幫手重新評估網路狀態,確認現在可以連線到 canary URL,並將右上角的按鈕變更為「完成」。
Q2. 一個體育場營運團隊 [Events] 想要推出一個可收集行銷訂閱的訪客 WiFi 網路。合規官員堅持該入口網站必須 100% 符合 GDPR 規範。開發團隊展示了一個原型,其中登入表單有一個預先勾選的方塊,上面寫著「我同意服務條款並同意接收行銷電子報」。為什麼此設計不合規?在保持高轉換率的同時,應如何重構 HTML/CSS 和表單結構以滿足 GDPR 要求?
提示:請考慮 GDPR 對於明確同意、行銷訂閱與服務條款之解耦,以及法律條文在行動裝置螢幕上的物理可見性的嚴格要求。
查看標準答案
這項提議的設計在兩個主要方面違反了 GDPR:首先,預先勾選的核取方塊不構成有效的同意,同意必須是自由給予、具體、知情且明確的。其次,將行銷同意與服務條款協議捆綁在一起是不合規的;不能強制使用者接受行銷電子郵件,以此作為使用 WiFi 服務的條件。
重構策略:
- 拆分同意書:將核取方塊拆分為兩個獨立的核取方塊。核取方塊 A 為必填,涵蓋服務條款與隱私權政策。核取方塊 B 為選填,涵蓋訂閱行銷電子報。
- 預設為取消勾選:確保 HTML 中的兩個核取方塊預設皆為未勾選狀態(省略
checked屬性)。 - CSS 可見性:由於超過 90% 的使用者使用行動裝置,請將核取方塊直接置於「連線」按鈕上方,使其在不滾動的情況下處於「首屏」可見位置。使用系統字型集,並將標籤字型大小設定為 14px,行高設定為 1.4 以利閱讀。
- 條款滾動框:為避免法律條文將表單元素擠出螢幕,請將詳細的服務條款放置在具有固定高度的可滾動容器中(
max-height: 100px; overflow-y: auto; background-color: #F5F1ED; border: 1px solid #D1D5DB; border-radius: 6px;),該容器可透過文字連結切換展開或關閉。這樣可以在維持乾淨、高轉換率版面的同時,確保絕對符合法律合規性。
Q3. 某家零售連鎖品牌 [Retail](/industries/retail) 正在 100 家分店部署自訂程式碼的歡迎頁面。設計師使用了 Google Fonts (Montserrat),並在 HTML 的 head 中連結了 CDN 代管的 Bootstrap 樣式表。在企業網路測試期間,頁面轉譯效果非常完美。然而,當部署到配置了 Captive Portal 的測試分店 AP 時,頁面轉譯卻出現了未套用樣式的 Times New Roman 文字、排版錯亂以及圖示缺失。為什麼會發生這種情況?該如何重構這些資產?
提示:分析使用者通過驗證前的網路連線狀態,並確定瀏覽器如何處理對圍牆花園(Walled Garden)以外網域的外部 HTTP 請求。
查看標準答案
發生此失敗的原因是,當載入歡迎頁面時,裝置處於未經驗證的受限狀態。在此狀態下,無線閘道器會封鎖所有連外網路流量,僅允許對閘道器圍牆花園(Walled Garden)中明確列入白名單的網域進行請求。由於 Bootstrap (cdn.jsdelivr.net) 和 Google Fonts (fonts.googleapis.com) 的 CDN 網域未列入白名單,瀏覽器擷取樣式表和字型檔案的請求會默默失敗。因此,瀏覽器會退回到其預設的轉譯引擎,導致 HTML 未套用樣式(呈現 Times New Roman 文字)且版面錯亂。
重構策略:
- 內聯 CSS:移除外部 Bootstrap 樣式表連結。將所需的 CSS grid/flexbox 規則直接複製到 HTML
<head>中的<style>區塊中。這可確保所有版面配置指令都包含在初始的單一頁面酬載(Payload)中。 - 實作系統字型集:移除 Google Fonts 的
@import或<link>呼叫。在 CSS 中將其替換為原生系統字型集(font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;)。這會強制裝置使用作業系統中已預先安裝的高品質字型,從而完全消除對外部網路的依賴。 - Base64 編碼圖示/標誌:如果版面依賴外部圖片或圖示庫(如 FontAwesome),請將這些圖示轉換為 SVG 格式,並將其內嵌在 HTML 中,或作為 CSS 中的 Base64 Data URI。這能保證頁面 100% 自帶完整內容,即使在完全沒有網路連線的情況下也能完美轉譯。
繼續閱讀本系列
Ruijie 的 Captive Portal:搭配 Purple 訪客 WiFi 進行設定
說明 Purple 的雲端訪客 WiFi 如何透過網頁驗證和 RADIUS(自命令列設定)部署於 Ruijie RG 系列基地台之上,以及在哪裡可以找到確切的設定步驟。
設計 B2B Captive Portals:收集註冊姓名與公司資料
本指南為 IT 經理與場域營運商提供了一個與廠商無關的技術框架,用於設計 B2B captive portals。指南詳細說明了如何規劃註冊欄位以擷取註冊姓名和公司資料,在確保高填答率的同時,維持 GDPR 合規性並建立企業帳戶級別的情報。
Captive Portal 架構:安全性、重新導向與最佳實踐
一份關於企業級 Captive Portal 架構的權威技術參考指南。本指南為部署安全且富含數據的訪客 WiFi 網路的 IT 主管,深入剖析網路隔離、DNS 重新導向、RADIUS 認證以及安全合規性。