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
| Route | Typical use | StillOnline note |
|---|---|---|
GET /up | Laravel 11 default liveness | Fine if it stays public and unauthenticated |
GET /health | Team convention, API docs | Same probe semantics — pick one canonical URL |
GET /api/health | API-only product behind prefix | Document 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?
| Strategy | Returns 503 when | Tradeoff |
|---|---|---|
| Liveness only | PHP-FPM dead | Green while queue workers stopped |
Redis ping in /health | Redis unreachable | Noisier during brief cache blips |
Separate /health/queue | Worker heartbeat stale | Needs 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:
- Embed worker heartbeat — worker touches cache/DB;
/healthreadslast_worker_okand returns 503 when stale (workers guide). - Second StillOnline check — small public route that only workers refresh via sidecar HTTP or shared state.
- 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
| Problem | Fix |
|---|---|
/health behind auth | Exclude path in middleware |
| Maintenance mode returns 503 | Expected during deploy — post scheduled maintenance |
| Laravel Octane worker stuck | Lightweight /health; restart policy is separate from HTTP probes |
Wrong APP_URL in redirects | Probes follow redirects — aim StillOnline at final HTTPS URL |
Register in StillOnline
- Sign in → new project.
- HTTP check → production
/health(or/up), GET, expected 200, five-minute interval on Free. - Share the status page in API docs — health quickstart.
- 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
- Health endpoint design
- Next.js SaaS health check monitoring
- Monitoring background workers and queues
- Health check URL quickstart
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.