← 回到維運筆記

Laravel Pulse 導入與多 Guard 403 排查

Laravel Pulse 是什麼

Laravel Pulse 是 Laravel 官方的效能監控套件。它把數據存進自己的資料表,並提供儀表板(路徑 /pulse),可以看 CPU / Memory、慢請求、慢 query、queue、cache 命中率和例外。

資料由常駐指令 php artisan pulse:check 收集。沒跑這支,儀表板打得開,但數據不會更新。

跟 Telescope 的分工:Telescope 逐筆記錄請求內容,只適合開發機。Pulse 只存聚合統計,開銷小,適合正式機。兩者可以併用。

環境需求:PHP ^8.1、Laravel ^10.48.4 起(Pulse v1.8,2026-10)。

部署步驟

以下以 Ploi 代管、PHP 8.1 的 Laravel 10 站台為例。

1. 安裝套件

composer require laravel/pulse
php artisan vendor:publish --provider="Laravel\Pulse\PulseServiceProvider"
php artisan migrate

vendor:publish 會產生 config/pulse.php 與 dashboard view。Pulse 需要 PHP ^8.1。

注意 PHP 版本對齊:若本機 PHP 比正式機新,composer.json 的 config.platform.php 要釘在正式機版本(例如 8.1.2),讓 lock 檔按正式環境解析相依;正式機一律 composer install 吃 lock,不跑 composer update。

2. 設定存取權限

Pulse 預設只允許本地環境存取。正式環境沒定義 viewPulse gate 時,/pulse 一律 403。

在 app/Providers/AuthServiceProvider.php 定義:

Gate::define('viewPulse', function (?\Illuminate\Contracts\Auth\Authenticatable $user = null) {
    $admin = auth()->guard('backend')->user();

    if (! $admin instanceof \App\Models\AdminUser || (int) $admin->status !== 1) {
        return false;
    }

    return in_array($admin->admin_group_id, config('permission.super_admin_groups', []))
        || $admin->permissions()->where('name', 'viewPulse')->where('status', 1)->exists()
        || $admin->permission_overrides()->where('name', 'viewPulse')->where('status', 1)->exists();
});

這段假設後台走獨立的 backend guard、模型為 AdminUser,放行規則:超管群組直接放行,其他帳號需 viewPulse 權限或覆寫,停用帳號與訪客一律 403。

兩個細節:

3. 部署到正式機

composer install --no-dev --optimize-autoloader --no-interaction
php artisan migrate --force
php artisan config:clear
php artisan route:clear
sudo systemctl reload php8.1-fpm

deploy script 若有 config:cache / route:cache,必須在 Pulse 裝好後重建,否則舊路由快取裡沒有 /pulse。

4. 建立資料收集 Daemon

Ploi → Site → Daemons → Add:

欄位 值
Command php artisan pulse:check
Directory 專案根目錄
User ploi
Processes 1

非 Ploi 環境用 Supervisor 或 systemd 跑同一支指令即可。

5. 驗收

日常維運與排查速查

症狀 處理
超管也一律 403 檢查 gate 第一參數是否為 ?Authenticatable $user = null,見踩坑
帳號對但 403 tinker 查 admin_group_id 與 status;status 非 1 是停用,群組不符補 viewPulse 權限
/pulse 回 404 route:list --path=pulse;路由不在就 route:clear 後重建快取
改完程式碼沒反應 systemctl reload php8.1-fpm,OPcache 咬舊版
儀表板全無數據 pgrep -af "pulse:check",Daemon 沒跑就重建
debug log 印不出來 .env 的 LOG_LEVEL;error 級別會濾掉 info/warning,臨時 debug 要用 Log::error

站台 access_log off 時,用回應 header 判斷請求走到哪一層:curl -sI 回應帶 session cookie 與 XSRF-TOKEN → 已到 Laravel,問題在 app 層;無 cookie → 沒進 PHP,往 nginx 查。

踩坑:多 guard 下一律 403

症狀:部署後 /pulse 一律 403。超管帳號正確,程式已部署,FPM 與快取都清過,gate 內連 Log::error 都沒輸出。

根因:gate 最初寫成零參數 closure:

Gate::define('viewPulse', function () {
    $admin = auth()->guard('backend')->user();
    // ...
});

Laravel 對訪客只有一個規則:gate closure 的第一個參數允許 null,才會執行 gate。否則直接判 false,closure 不會被呼叫。

Pulse 用預設 web guard 解析使用者,但後台管理員登在 backend guard。在 web guard 眼裡,管理員全是訪客,gate 被無聲跳過。

「連 Log::error 都沒輸出」就是關鍵證據:closure 根本沒執行。

修正:第一個參數改成 ?Authenticatable $user = null。Laravel 看的是參數型別「允不允許 null」,不是這次傳的值。參數本身用不到,授權靠自己解析的 backend guard。

測試盲點:actingAs('backend') 會把預設 guard 切成 backend,$user 不是 null,gate 照常執行,模擬不出訪客路徑。要用真實登入流程(POST 登入端點、帶 session 請求 /pulse)才重現得出 403。多 guard 架構測授權,不能只靠 actingAs。

已排除:status 嚴格比對、OPcache、網域與 session 設定、route cache、超管群組設定、LOG_LEVEL 過濾。這些都試過,403 依舊。