
有些網站會在角落顯示「目前線上 12 人」,讓訪客感覺網站很熱鬧。網路上的舊教學多半是這樣做:每次有人打開網頁,PHP 就記下他的 IP,再數最近幾分鐘有幾個不同的 IP。
這個做法現在有兩個問題:
- 網站開了快取就不準。快取外掛會把網頁存成固定的檔案,訪客拿到的是存好的頁面,PHP 根本沒有執行,人數就停在快取那一刻。
- 記錄 IP 是個人資料,存了就要寫進隱私權政策,最好能不存就不存。
這篇換一種做法:網頁載入後,由訪客的瀏覽器每分鐘回報一次「我還在」,主機只記一組隨機產生的代號和時間,算出最近 5 分鐘有幾個人。
程式碼
放在子佈景主題的 functions.php,或用 Code Snippets 這類外掛新增:
PHP
// 1. 接收訪客瀏覽器每分鐘送來的「我還在」,回傳最近 5 分鐘內的人數
add_action( 'rest_api_init', function () {
register_rest_route( 'zw/v1', '/online', array(
'methods' => 'POST',
'permission_callback' => '__return_true',
'callback' => function ( WP_REST_Request $request ) {
$id = preg_replace( '/[^a-f0-9]/', '', strtolower( (string) $request->get_param( 'id' ) ) );
$visitors = get_transient( 'zw_online' );
$visitors = is_array( $visitors ) ? $visitors : array();
$now = time();
foreach ( $visitors as $key => $time ) {
if ( $now - $time > 5 * MINUTE_IN_SECONDS ) {
unset( $visitors[ $key ] ); // 超過 5 分鐘沒消息,當作離開了
}
}
if ( 16 === strlen( $id ) ) {
$visitors[ $id ] = $now;
}
$visitors = array_slice( $visitors, -5000, null, true ); // 最多記 5000 人,避免被亂送資料塞爆
set_transient( 'zw_online', $visitors, 10 * MINUTE_IN_SECONDS );
return array( 'online' => count( $visitors ) );
},
) );
} );
// 2. 短代碼 [zw_online]:先放一個隱藏的位置,由瀏覽器取得人數後再顯示
add_shortcode( 'zw_online', function () {
wp_register_script( 'zw-online', false, array(), null, true );
wp_enqueue_script( 'zw-online' );
wp_add_inline_script( 'zw-online', 'window.zwOnlineApi = ' . wp_json_encode( rest_url( 'zw/v1/online' ) ) . ';' . <<<'JS'
(() => {
const el = document.querySelector('.zw-online');
if (!el) return;
let id = '';
try { id = localStorage.getItem('zwOnlineId') || ''; } catch (e) {}
if (!/^[a-f0-9]{16}$/.test(id)) {
id = Array.from(crypto.getRandomValues(new Uint8Array(8)), (n) => n.toString(16).padStart(2, '0')).join('');
try { localStorage.setItem('zwOnlineId', id); } catch (e) {}
}
const ping = () => fetch(window.zwOnlineApi, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ id }),
})
.then((res) => res.json())
.then((data) => { el.querySelector('b').textContent = data.online; el.hidden = false; })
.catch(() => {});
ping();
setInterval(() => { if (!document.hidden) ping(); }, 60000);
})();
JS
);
return '<span class="zw-online" hidden>目前線上:<b></b> 人</span>';
} );在想顯示的地方放一個「短代碼」區塊,填 [zw_online]。頁尾、側欄的小工具也可以放。
程式在做什麼
主機這一端(第一段)
- 註冊一個 REST API 網址
/wp-json/zw/v1/online,只接受 POST。 - 收到代號後,先把超過 5 分鐘沒消息的人刪掉,再記下這個人現在的時間,回傳人數。
- 代號只接受 16 個英文數字(0~9、a~f),其他格式的資料直接忽略;最多只記 5000 人,避免有人一直送假資料把它塞爆。
- 資料存在 WordPress 的暫存資料(transient)裡,10 分鐘沒人回報就自動消失。
瀏覽器這一端(第二段的 JavaScript)
- 第一次來的訪客,隨機產生一組代號存在瀏覽器裡(localStorage),之後同一個瀏覽器都用同一組。所以同一個人開好幾個分頁,只算一個人。
- 網頁打開時回報一次,之後每分鐘回報一次。分頁切到背景、沒在看的時候不回報。
- 拿到人數才把文字顯示出來。瀏覽器關掉 JavaScript、或連線失敗時,這段文字一直是隱藏的,不會出現「目前線上: 人」這種空白。
因為人數是瀏覽器另外去問的,網頁本身被快取也沒關係。
實際測試的結果
- 用 3 個不同的瀏覽器依序打開頁面:分別顯示 1 人、2 人、3 人。
- 第 1 個人在同一個瀏覽器再開一個分頁:還是 3 人,沒有重複計算。
- 直接送 4 筆格式不對的代號給 API:人數維持 3 人。
- 用 GET 方式打開 API 網址:WordPress 回應找不到(只接受 POST)。
- 模擬其他人都 6 分鐘沒有回報,第 3 個人重新整理頁面:變成 1 人。
- 關掉 JavaScript 打開頁面:線上人數的文字保持隱藏。
這是「大約」的人數
- 同一個人用手機和電腦各開一次,會算成兩個人。
- 兩個人在同一瞬間回報時,其中一筆可能被另一筆蓋掉,少算一個;下一分鐘回報就會補回來。
- 「5 分鐘內」是自己定的,改程式裡的
5 * MINUTE_IN_SECONDS就能調整。
拿來營造氣氛可以,不要拿它當正式的流量數據。
什麼情況不建議用
- 流量很大的網站:每位訪客每分鐘都會打一次主機,線上 300 人就是每分鐘 300 次請求,而且這些請求都要執行 PHP,主機負擔不小。
- 流量很小的網站:大部分時間都顯示「目前線上:1 人」(就是正在看的那個人),反而顯得冷清。
- 想知道準確的流量:用 Google Analytics 的「即時」報表,能看到現在有多少人、在看哪一頁、從哪裡來,比自己數準確很多,也不會增加網站負擔。
舊教學裡常一起出現的「您是第幾位訪客」累計計數器,也不建議再做:被機器人、重新整理灌高的數字沒有意義,訪客也不太在意。
延伸閱讀
- WordPress 顯示文章閱讀次數:不用外掛的程式碼寫法(含快取網站解法)
- PHP 點擊計數器:用 txt 檔記錄連結被點了幾次的安全寫法(附 GA4 替代做法)
- 網站上線後的 SEO 提交流程 6 步
網站熱不熱鬧,最後還是要看有沒有帶來客人。正威網路行銷是 25 年經驗的基隆網頁設計工作室,網站管家年費包含每半年一次網站健檢報告,看流量和 Google 搜尋表現,台北與全台客戶也歡迎來聊聊。







發表留言
還沒有人留言,歡迎第一個分享你的想法或問題。留言經審核後才會顯示,Email 不會公開。