Nuxt's superpower is that one codebase can serve routes four different ways at once. With a few lines of routeRules, your marketing pages are prerendered, your docs are cached with stale-while-revalidate, your dashboard is a client-only SPA, and everything else is server-rendered on demand. That's great for performance and quietly terrible for monitoring, because each route class fails differently — and two of the four can be completely broken while returning a healthy 200.

This guide covers monitoring Nuxt the way it actually deploys: one monitor per route class, a Nitro health endpoint, the API routes behind your client-rendered pages, and the preset-specific failure modes — the same "deployment decides the failure modes" logic as our SvelteKit guide, with Nuxt's own twist: the 200s that lie.

One Nuxt app is several apps

Start by reading your routeRules (and any ssr: false pages), because they define what you're monitoring:

  • SSR routes (the default). Rendered per-request by Nitro. They fail loudly — a throwing data fetch or a dead database becomes a real 500 that an HTTP monitor sees. Monitor one page that exercises your real data layer.
  • Prerendered routes. Built to static files, served without touching your server. They survive an application crash and fail instead on DNS, TLS, CDN config, and bad deploys — the static-site failure axis. Monitor one separately; the SSR/static pair is diagnostic when one is down and the other isn't.
  • SWR/ISR-cached routes. The sneaky class. A cached route serves 200s from the cache even when regeneration has been failing for days — the page is "up" and increasingly wrong. Up/down monitoring is structurally blind here; the section on staleness below covers what works.
  • Client-only routes (ssr: false). The server sends an empty shell and JavaScript builds the page. The shell is always 200. The page can be a blank screen, a spinner forever, or an error toast — the monitor watching the URL sees none of it. You monitor these by monitoring their APIs.

The monitor set

1. A Nitro health endpoint

Nuxt's server routes make this a one-file job:

// server/api/health.get.ts
export default defineEventHandler(async (event) => {
  const checks = { database: 'unknown' };
  let healthy = true;

  try {
    await db.execute('SELECT 1');
    checks.database = 'ok';
  } catch {
    checks.database = 'down';
    healthy = false;
  }

  setResponseStatus(event, healthy ? 200 : 503);
  return { status: healthy ? 'ok' : 'degraded', checks };
});

The standard health-endpoint rules apply: hard dependencies gate the status code, checks stay cheap because this runs every minute, and a keyword assertion on "status":"ok" keeps a proxy's generic 200 from impersonating health.

2. One page per route class you use

An SSR page, a prerendered page, a cached page. Three monitors, three failure axes, and when something breaks the pattern of greens and reds tells you which layer to look at before you've opened a terminal.

3. The API routes behind client-rendered pages

If your dashboard is ssr: false or leans on lazy useAsyncData, the page monitor is testing almost nothing — the shell renders from the CDN whether or not your backend exists. Put monitors directly on the /api/* endpoints the page calls. This is the same inversion as monitoring BaaS-backed apps: when rendering moves to the client, uptime lives in the API layer.

The subtle variant is worth spelling out: a page using lazy: true data fetching returns its 200 before the data arrives. If the fetch then fails, users get a broken page on a healthy status code. A keyword check asserting on content that only renders after data loads closes the gap for server-rendered output; for client-only rendering, monitor the API itself.

SWR/ISR: monitor for staleness, not downness

A cached route whose regeneration fails doesn't go down — it fossilizes. The cache keeps answering with the last good render, prices drift, published posts don't appear, and every check comes back 200. Two defenses:

  • Content-staleness checks. CronAlert's content monitoring can alert when a page's content stops changing — point it at a cached route that should update at least every cache window (or assert on a rendered build-stamp/date string) and silence becomes a signal.
  • Monitor the regeneration's dependency. Regeneration usually fails because the CMS, database, or upstream API behind it is failing. A direct monitor on that source (often via your health endpoint) catches the cause while the cache is still masking the symptom — the same reasoning as headless CMS monitoring.

Preset-specific failure modes

  • node-server (default build): you own a long-running Node process — restart policy (systemd/Docker/pm2), then external verification that it worked; proxy misconfiguration; slow memory leaks that show up as a rising response-time trend before they're an outage. The Node.js guide's failure catalog applies wholesale.
  • Vercel/Netlify presets: no process to die; instead cold starts (set timeout thresholds accordingly, use consecutive-check confirmation) and per-function duration ceilings on your slowest data-heavy routes.
  • Cloudflare preset: CPU-time limits and runtime bindings — the Cloudflare Workers guide applies directly.
  • Static (nuxi generate): nothing to crash, everything to verify — a generate run that half-fails can ship broken HTML that 200s forever. Keyword-check a string only a correct build produces.

One more global one: server/middleware/ runs on every request. An exception there — a bad import, a throwing auth check — takes down every server-rendered route at once, exactly like a broken hooks.server.ts in SvelteKit or middleware in Next.js. Your SSR page monitor is the tripwire for the whole class.

Set it up in ten minutes

  • Create a CronAlert account — the health endpoint, per-route-class pages, and API monitors all fit on the free plan (25 monitors, 3-minute checks). Keyword and content-staleness checks are Pro at $5/mo.
  • Add server/api/health.get.ts and point a monitor at it.
  • Add one monitor per route class in your routeRules, plus the two or three API routes your client-rendered pages depend on.
  • If you use SWR/ISR on anything business-critical, add a staleness check.
  • Route alerts to Slack or email, send a test alert, and fire-drill it once before you trust it.

Frequently asked questions

What should I monitor in a Nuxt app?

One route per route class (SSR, prerendered, cached), a /api/health endpoint with dependency checks, and the API routes behind any client-rendered pages.

Why does my broken page still return 200?

The failure is client-side or cache-side: SPA shells and lazy data fetches send the 200 before anything can go wrong, and SWR caches keep serving the last good render. Monitor the APIs and assert on content, not just status codes.

How do I add a health endpoint?

A defineEventHandler in server/api/health.get.ts probing hard dependencies and returning 503 when one is down. Code above.

Does the Nitro preset change monitoring?

Yes — node-server means process-level concerns, serverless presets mean cold starts and timeouts, static means content verification. Same principle as SvelteKit adapters.

How do I monitor SWR/ISR routes?

Staleness checks on the rendered output plus a monitor on the data source regeneration depends on. Up/down checks can't see a cache serving stale 200s.

Monitor the routes you shipped, not the framework

Nuxt lets every route pick its own rendering contract, so your monitoring has to be a portfolio, not a single homepage check. Read your routeRules, place one monitor per class, watch the APIs behind the client-rendered parts, and let content checks cover what status codes can't. Set up CronAlert free — the whole portfolio for a typical Nuxt app is six to ten monitors, well inside the free plan.

Related reading: SvelteKit monitoring, Next.js and Vercel monitoring, Astro monitoring, static site monitoring, and designing health check endpoints.