← 使用情境

搶票與限量開賣的排隊等候室

在開賣頁前排一條公平、即時的隊伍:訪客看著自己的位置前進,依你設定的速度進場,你的網站驗證簽名 pass。任何主機都能用,包括 Laravel 與 Filament。

搶票、限量發售、預購開跑,一整天的流量會擠在開賣的第一分鐘。自動擴展來不及反應,結帳對每個人都變慢,最後拼的是誰的網速快。等候室在最忙的頁面前排一條平靜、公平的隊伍:訪客拿到號碼、即時看著隊伍前進,依你的網站撐得住的速度進場。自己註冊、自己設定,不用找業務,也不用換主機或 CDN。

訪客會看到什麼

  • 排隊號碼與「前面還有 4,812 人」,等待期間即時更新。
  • 重新整理或多開分頁,位置不會掉,也不會往前插隊。
  • 託管等候頁支援英文、繁體中文、簡體中文、日文、韓文、泰文與越南文。
  • 開賣前有清楚的倒數,賣完時清楚顯示「已售完」。
託管等候頁:訪客的排隊位置、前面人數、預估等待時間與商家公告,以訪客的語言顯示。
託管等候頁:訪客的排隊位置、前面人數、預估等待時間與商家公告,以訪客的語言顯示。

你會得到什麼

  • 依你設定的速度前進的隊伍,例如每分鐘 300 人。
  • 關鍵時刻用得到的按鈕:暫停隊伍、立刻放 500 人進來、庫存賣完就關閉。
  • 每位放行的訪客都會拿到簽名 pass,你的網站驗證後才提供受保護的頁面,猜網址也插不了隊。
  • 需要的話,用 pass 裡的 ticket ID 做到一個號碼只能結帳一次。
  • 排隊中的訪客不佔用 app 的連線數,搶購期間網站其他即時功能照常有餘裕。
  • 對所有排隊的人發公告,例如「A 區已售完」,等候頁立刻顯示。
主控台裡的等候室:即時人數、放行速度、預估清空時間,以及暫停、放行、售罄與公告按鈕。
主控台裡的等候室:即時人數、放行速度、預估清空時間,以及暫停、放行、售罄與公告按鈕。

等候室是任何 Hibira 方案都能加購的附加功能。一個房間、每月 5,000 位排隊訪客免費;付費方案每月 $29 起,單場活動票 $49 可用 72 小時。超過方案用量時,多出的訪客從點數扣抵,不會有人被擋在門外。詳見價格。

用到的功能

  • Hibira app 裡的等候室,在主控台或用簽名的 HTTP API 管理。
  • 託管等候頁 ws.hibira.io/wait/APP_KEY/ROOM,或用 @hibira-io/waiting-room 自建等候頁。
  • 每個房間一個 cache 頻道(cache-waitroom-ROOM):一則訊息告訴所有等候中的訪客隊伍走到哪裡,每個瀏覽器自己算出位置。
  • 用你的 app secret 簽名(HMAC-SHA256)的 pass,你的網站自己驗證,不必呼叫 Hibira。
訪客開啟 /drop
沒有 pass:導向
Hibira託管等候頁
輪到了:?hibira_pass=…
你的網站驗證 pass、設定 cookie

讓隊伍前進

你的團隊主控台、Filament 或 API
開啟、暫停、放行、關閉
Hibiracache-waitroom-presale
一則更新
所有等候中的訪客各自算出位置
沒有 pass 的訪客先排隊,輪到時帶著簽名 pass 回來

選擇接入方式

依網站跑在哪裡來選。越上面的方式越難被繞過。

步驟 1:建立房間

在主控台建立,或用簽名的 HTTP API 建立,簽名方式和發布事件相同:

POST /apps/APP_ID/waiting_rooms/presale
{
  "admit_per_minute": 300,
  "state": "paused",
  "starts_at": "2026-12-04T12:00:00+09:00",
  "title": "Winter tour presale",
  "return_urls": ["https://shop.example.com/"]
}

open 依速度放行、paused 先把隊伍停住、closed 在賣完時停止發號碼。在 starts_at 之前到的訪客會拿到號碼並看到倒數,時間一到隊伍才開始前進。

步驟 2:在 edge 保護頁面

在 Cloudflare Workers、Next.js middleware、Deno 或 Bun 上,一個呼叫就搞定:

// middleware.js (Next.js)
import { NextResponse } from 'next/server';
import { protect } from '@hibira-io/waiting-room/server';

export async function middleware(request) {
  const blocked = await protect(request, {
    appKey: process.env.HIBIRA_KEY,
    secret: process.env.HIBIRA_SECRET,
    room: 'presale',
  });
  return blocked ?? NextResponse.next();
}

export const config = { matcher: ['/drop/:path*'] };

沒有 pass 的訪客會被導到等候頁。輪到時帶著 pass 回來,protect() 驗證後存進 HttpOnly cookie。Express 請用同一個套件裡的 expressWaitingRoom()。

步驟 3:Laravel

使用 hibira-io/laravel 0.4 以上版本(你可能已經用它做廣播),用指令建立房間,再用一個middleware 保護路由:

composer require hibira-io/laravel:^0.4
php artisan hibira:waiting-room presale create --rate=300 --title="Winter presale"
Route::middleware('hibira.waiting:presale')->group(function () {
    Route::get('/drop', [DropController::class, 'show']);
    Route::post('/drop/checkout', [DropController::class, 'checkout']);
});

沒有 pass 的訪客會被導到等候頁,語言跟你的 app 一致。訪客帶回來的 pass用你的 app secret 驗證,不需要呼叫 API,並存在 HttpOnly cookie 裡直到過期。沒有 pass 的 POST 會得到 403。在 controller 裡可以從 request 取得 pass :

$pass = $request->attributes->get('hibira_pass');
$pass->number;   // queue number
$pass->ticketId; // record it at checkout: one order per number

在程式、排程工作或指令列都能操作房間:

use Hibira\Laravel\Facades\Hibira;

Hibira::waitingRoom('presale')->pause();
Hibira::waitingRoom('presale')->admit(500);
Hibira::waitingRoom('presale')->notice('Zone A is sold out');
Hibira::waitingRoom('presale')->close();

本機開發時設定 HIBIRA_WAITING_ROOMS=false,就不用排隊。

步驟 4:在 Filament 裡操作隊伍

開賣當天,團隊不需要再多開一個後台。使用 hibira-io/filament 0.5 以上版本,一行就能在後台加上「等候室」頁面:

HibiraPlugin::make()->waitingRooms(
    authorize: fn ($user) => $user->is_admin,
)

這個頁面即時顯示每個房間的排隊人數、放行進度和速度。團隊可以在這裡暫停、開放、立即放行、發布公告、售完時關閉房間,也能建立或修改房間。不寫 authorize 的話,所有後台使用者都能進入。

步驟 5:自建等候頁

託管等候頁適合大多數網站。想完全符合自家品牌,可以自己做:

import { joinWaitingRoom } from '@hibira-io/waiting-room';

joinWaitingRoom({
  key: 'APP_KEY',
  room: 'presale',
  onUpdate: ({ ahead, etaMinutes }) => render(ahead, etaMinutes),
  onTurn: (turn) => turn.redirect('https://shop.example.com/drop'),
  onClosed: () => showSoldOut(),
});

用戶端會把訪客的 ticket 存在瀏覽器裡,重新整理也不會掉位置。如果連不上,它會改成每隔幾秒查一次狀態,不會有人被擋在外面。

步驟 6:在 App 裡排隊

手機或遊戲用戶端用的是和託管等候頁相同的公開端點:加入隊伍、訂閱房間的 cache 頻道、輪到時換 pass,之後在 API 呼叫中帶上 pass。你的 API 用 app secret 驗證 pass,不需要呼叫 Hibira;在 Laravel 上用 WaitingRoom::verifyPass($pass, 'presale') 即可。

POST /waitroom/APP_KEY/presale/join         -> {"ticket": "…", "number": 5121}
GET  /waitroom/APP_KEY/presale              -> {"admitted": 4980, "state": "open", …}
POST /waitroom/APP_KEY/presale/pass         {"ticket": "…"}  -> {"pass": "…"}

與 Queue-it、Cloudflare Waiting Room 比較

依據 2026 年 11 月各家公開的價格與方案頁面。方案會變動,做決定前請再向各家確認。

完整比較(含價格與各自適合的情況):Queue-it、Cloudflare Waiting Room、CrowdHandler、Queue-Fair。

小提醒

  • 保護真正會賣完的頁面:商品頁、購物車與結帳。首頁和圖片放在等候室外面。
  • 在關鍵的請求上驗證 pass,不要只在瀏覽器檢查。訪客可以改自己頁面上的任何腳本,但偽造不了簽名。
  • 如果一個號碼只能買一次,結帳時記下 pass 的 ticket ID,拒絕用同一個 ID 下第二筆訂單。
  • 開賣前 15 到 30 分鐘就開啟房間並設定 starts_at。早到的訪客會在倒數頁等,而不是在你的網站狂按重新整理。
  • 一開始先用結帳撐得住的速度,一邊看錯誤率一邊往上調。暫停永遠只差一個按鈕。