靜態 HTML 頁面 (CDN 緩存、Nginx 直接服務) 無法直接讀取 Session/Cookie。通過 <script src="auth_status.php"> 讓 PHP 動態輸出 JavaScript 代碼,實現「靜態頁 + 動態用戶狀態」。
[靜態 HTML] --> 瀏覽器解析 --> 請求 auth_status.php?callback=xxx
↓
[PHP 讀取 Session/Cookie]
↓
輸出 document.write('...') 或 callback({...})
↓
瀏覽器執行 JS 更新 DOM
<?php
// auth_status.php
header('Content-Type: application/javascript; charset=UTF-8');
// 1. 讀取用戶狀態(示例:Session)
session_start();
$isLoggedIn = !empty($_SESSION['user_id']);
$userName = $isLoggedIn ? htmlspecialchars($_SESSION['user_name']) : '';
$userId = $isLoggedIn ? (int)$_SESSION['user_id'] : 0;
// 2. 支援 JSONP 回調(跨域場景)
$callback = $_GET['callback'] ?? null;
if ($callback && !preg_match('/^[a-zA-Z_][a-zA-Z0-9_]*$/', $callback)) {
$callback = null; // 防 XSS
}
// 3. 構建數據
$data = [
'loggedIn' => $isLoggedIn,
'userId' => $userId,
'userName' => $userName,
'avatar' => $isLoggedIn ? '/avatar/' . $userId . '.jpg' : '',
'logoutUrl'=> '/logout.php',
'loginUrl' => '/login.php?redirect=' . urlencode($_SERVER['HTTP_REFERER'] ?? '/'),
];
// 4. 輸出
if ($callback) {
// JSONP
echo $callback . '(' . json_encode($data, JSON_UNESCAPED_UNICODE) . ');';
} else {
// 直接輸出 JS 變量/函數調用(同域)
echo "window.__USER_STATUS__ = " . json_encode($data, JSON_UNESCAPED_UNICODE) . ";";
// 或直接寫 DOM(簡單但不推薦,阻塞渲染)
// echo "document.getElementById('user-area').innerHTML = " . json_encode($html) . ";";
}
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>靜態頁面</title>
</head>
<body>
<div id="user-area">載入中...</div>
<!-- 同域:直接定義全局變量 -->
<script src="/auth_status.php"></script>
<script>
// 等待變量就緒 (腳本異步加載,需監聽)
function initUserArea() {
if (!window.__USER_STATUS__) return setTimeout(initUserArea, 50);
const s = window.__USER_STATUS__;
const el = document.getElementById('user-area');
if (s.loggedIn) {
el.innerHTML = `歡迎, <a href="/profile?id=${s.userId}">${s.userName}</a> | <a href="${s.logoutUrl}">退出</a>`;
} else {
el.innerHTML = `<a href="${s.loginUrl}">登錄</a> / <a href="/register.php">註冊</a>`;
}
}
initUserArea();
</script>
</body>
</html>
靜態頁在 static.example.com,認證服務在 api.example.com:
<script>
function renderUser(data) {
const el = document.getElementById('user-area');
if (data.loggedIn) {
el.innerHTML = `歡迎, ${data.userName} | <a href="${data.logoutUrl}">退出</a>`;
} else {
el.innerHTML = `<a href="${data.loginUrl}">登錄</a>`;
}
}
</script>
<script src="https://api.example.com/auth_status.php?callback=renderUser"></script>
| 方案 | 優點 | 缺點 | 適用場景 |
|---|---|---|---|
| JS+PHP (本文) | 無框架依賴、兼容極舊瀏覽器、CDN 友好 | 阻塞渲染、XSS 風險、維護困難 | 老項目、郵件模板、純靜態托管 |
| AJAX + API | 非阻塞、關注點分離、易測試 | 需處理 CORS、額外請求 | 現代 SPA/SSR |
| SSI (Server Side Includes) | Nginx/Apache 原生、極快 | 需服務器配置、不支援動態邏輯 | 高性能靜態站點 |
| Edge Side Includes (ESI) | Varnish/Cloudflare 支援、緩存片段 | 基礎設施複雜 | 大型 CDN 架構 |
| Web Components / Shadow DOM | 封裝性好、框架無關 | 瀏覽器兼容性、需 Polyfill | 微前端、組件化 |
Content-Security-Policy: script-src 'self' 'nonce-...',配合 nonce 替代內聯腳本。HttpOnly; Secure; SameSite=Lax,JS 無法讀取,防 XSS 竊取。htmlspecialchars。callback 參數格式。JS+PHP 動態注入是解決「靜態頁面顯示動態用戶狀態」的經典折中方案。核心模式:靜態頁引入動態 JS → PHP 讀取會話 → 輸出數據/渲染代碼 → 前端更新 DOM。現代項目優先選 AJAX/API 模式,老系統或極簡場景仍可使用本模式,但務必做好 XSS 防護與 CSP 配置。