抢票与限量发售的排队等候室
在开售页前排一条公平、实时的队伍:访客看着自己的位置前进,按你设定的速度进场,你的网站验证签名 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。早到的访客会在倒数页等,而不是在你的网站不停刷新。 - 一开始先用结账撑得住的速度,一边看错误率一边往上调。暂停永远只差一个按钮。