← Blog

Laravel health check route monitoring for SaaS

Laravel powers many indie SaaS APIs — billing portals, webhooks, admin panels behind Sanctum or session auth. External uptime tools need a public GET route that returns 200 without a logged-in cookie, usually /health or Laravel 11’s built-in /up.

StillOnline probes that HTTPS URL from outside your VPS or PaaS, publishes a status page, and alerts owners via Telegram, Slack, or email when checks fail. Queue and Redis failures often need a separate check or a deeper handler — not every team folds them into one route.

Quick answer

Laravel routing lets you register Route::get('/health', …) returning 200 JSON in under two seconds on your production HTTPS hostname — or use the framework /up route in Laravel 11+. Register the full URL in StillOnline (Free: one project, one URL, five-minute probes per pricing). Keep /health lightweight; add optional queue or Redis pings on a second URL with Pro (up to 10 checks per project).

/health vs Laravel /up

RouteTypical useStillOnline note
GET /upLaravel 11 default livenessFine if it stays public and unauthenticated
GET /healthTeam convention, API docsSame probe semantics — pick one canonical URL
GET /api/healthAPI-only product behind prefixDocument the full path in README

Pick one path and use it in runbooks, OpenAPI, and StillOnline. Changing it breaks bookmarks and confuses integrators — see health endpoint design.

Minimal /health controller

Exclude the route from auth middleware and CSRF. Keep the handler fast — no heavy jobs on every probe.

// routes/web.php or routes/api.php
Route::get('/health', function () {
    return response()->json(['status' => 'ok'], 200);
});

Verify from outside your laptop:

curl -sS https://api.yourproduct.com/health

Laravel middleware runs on every request — add /health to $except on Authenticate and any global redirect that sends anonymous users to login.

Queue and Redis: one URL or two?

StrategyReturns 503 whenTradeoff
Liveness onlyPHP-FPM deadGreen while queue workers stopped
Redis ping in /healthRedis unreachableNoisier during brief cache blips
Separate /health/queueWorker heartbeat staleNeeds Pro second URL in StillOnline

Laravel queues run in worker processes, not in the web request cycle. A green /health does not prove php artisan queue:work is running. Options:

  1. Embed worker heartbeat — worker touches cache/DB; /health reads last_worker_ok and returns 503 when stale (workers guide).
  2. Second StillOnline check — small public route that only workers refresh via sidecar HTTP or shared state.
  3. Status page component — label “Background jobs” separately when web is up but queue is not.

Redis checks use Redis::connection()->ping() or Cache::store('redis')->get('ping') — timeout aggressively (1–2 s) so probes do not hang.

Deployment and Octane gotchas

ProblemFix
/health behind authExclude path in middleware
Maintenance mode returns 503Expected during deploy — post scheduled maintenance
Laravel Octane worker stuckLightweight /health; restart policy is separate from HTTP probes
Wrong APP_URL in redirectsProbes follow redirects — aim StillOnline at final HTTPS URL

Register in StillOnline

  1. Sign innew project.
  2. HTTP check → production /health (or /up), GET, expected 200, five-minute interval on Free.
  3. Share the status page in API docs — health quickstart.
  4. Settings → connect Telegram for owner alerts.

Pro ($9/mo) — split web liveness and queue/Redis deep check across two URLs. Compare stacks: Next.js health route.

Related guides

FAQ

Should StillOnline monitor Laravel /up or a custom /health?

Either works if the URL is public, returns 200 when the app serves traffic, and stays stable across deploys. Most teams standardize on /health in docs even when /up exists.

Does StillOnline run php artisan or SSH into my server?

No. StillOnline only performs HTTP GET from the public internet — same as curl from outside. You implement what “healthy” means in the route.

How fast does StillOnline alert when /health fails?

Default five-minute interval and two consecutive failures — roughly 10 minutes after sustained failure. Tune your route to fail fast when dependencies are dead; do not rely on sub-minute queue SLAs from HTTP probes alone.