搶票與限量開賣的排隊等候室
在開賣頁前排一條公平、即時的隊伍:訪客看著自己的位置前進,依你設定的速度進場,你的網站驗證簽名 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。
?hibira_pass=…讓隊伍前進
選擇接入方式
依網站跑在哪裡來選。越上面的方式越難被繞過。
| 網站跑在哪裡 | 怎麼接入 | 在哪裡檢查 |
|---|---|---|
| Cloudflare Workers、Vercel 上的 Next.js、Deno、Bun | @hibira-io/waiting-room 的 protect() | 頁面送出之前 |
| 自己的伺服器:Express、Laravel 等 | 一個驗證 pass 的 middleware | 路由執行之前 |
| 完全自訂的等候頁 | 瀏覽器端的 joinWaitingRoom(),再搭配上面任一種檢查 | 路由執行之前 |
| iOS、Android、Flutter 或 Unity App | 等候室的公開端點,再把 pass 放在 API header | 你的 API 裡 |
| 沒有自己的後端(託管商店) | 託管等候頁,加上結帳時檢查 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 比較
| Hibira | Queue-it | Cloudflare Waiting Room | |
|---|---|---|---|
| 怎麼開始 | 自己註冊、自己建立房間 | 聯絡業務;有一個公開的單場方案 | 需要 Business 或 Enterprise 方案 |
| 網站跑在哪裡 | 任何主機、CDN 或框架 | 任何主機,搭配他們的 connector | 流量要經過 Cloudflare 代理 |
| 排程開賣與倒數 | 有 | 有 | Enterprise |
| 多個房間 | 有 | 有 | Enterprise,加購 Advanced |
| 自訂等候頁 | 有,用小巧的瀏覽器用戶端 | 有 | Enterprise 自訂範本 |
| 在自己的後台操作隊伍 | 可以,例如 Filament 頁面 | 他們的後台 | Cloudflare 後台或 API |
依據 2026 年 11 月各家公開的價格與方案頁面。方案會變動,做決定前請再向各家確認。
完整比較(含價格與各自適合的情況):Queue-it、Cloudflare Waiting Room、CrowdHandler、Queue-Fair。
小提醒
- 保護真正會賣完的頁面:商品頁、購物車與結帳。首頁和圖片放在等候室外面。
- 在關鍵的請求上驗證 pass,不要只在瀏覽器檢查。訪客可以改自己頁面上的任何腳本,但偽造不了簽名。
- 如果一個號碼只能買一次,結帳時記下 pass 的 ticket ID,拒絕用同一個 ID 下第二筆訂單。
- 開賣前 15 到 30 分鐘就開啟房間並設定
starts_at。早到的訪客會在倒數頁等,而不是在你的網站狂按重新整理。 - 一開始先用結帳撐得住的速度,一邊看錯誤率一邊往上調。暫停永遠只差一個按鈕。