AppScan
Product · Performance

How fast it is, measured, not felt.

A real browser loads your pages the way a visitor's does and writes down every request it made. One page on the free scan, up to 20 on a paid plan, with Core Web Vitals taken from the page itself rather than a lab profile.

01The browser pass

Every request, attributed.

Headers tell you what a server promises. A browser tells you what your application actually does: which requests it makes, in what order, how many bytes they carry and who they go to.

  • Cold, then navigated

    Each route is loaded cold and then reached again through client navigation - 3 in-app links per page - so a refetch the framework should have cached shows up as one.

  • Routes, clustered

    Pages that are the same template with different data are grouped, so a hundred product pages are one route with one baseline.

  • Holding pages waited out

    A host that serves a self-reloading placeholder is given up to 9 seconds to hand over the real page before anything is judged.

  • Recorded, not screenshotted

    What is kept is the request log, the timings and the console - never a picture of the page.

checkout-app · / · cold loadLCP 3.9 s · 1.9 MB
  • /
    document · 41 KB
  • /_next/static/chunks/main.js
    script · 312 KB
  • cdn.segment.com/analytics.js
    script · 188 KB
  • /api/orders?status=open
    fetch · 148 KB
  • /api/orders/1041
    fetch · 6 KB
  • /api/orders/1042
    fetch · 6 KB
  • /api/orders/1043 … ×12
    fetch · 6 KB
  • images.unsplash.com/hero.jpg
    image · 1,420 KB
n+1 /api/orders/:id ×12third parties 61% of bytesfirst party
02What it flags

17 rules, each with a number on it.

A threshold is printed beside every rule, because a finding that says “slow” without saying what would have been fast enough is an opinion.

Core Web Vitals
LCP 2.5 s · CLS 0.1 · INP 200 ms

Measured on the real load, judged at Google's own boundaries, then poor at 4 s, 0.25 and 500 ms.

browser.performance.web-vitals
Slow load
4,000 ms

A page that takes longer than this to settle is reported with its timings.

browser.performance.slow-load
N+1 requests
8 per-item calls

A list request followed by one call per item to the same :id route; high at 30.

browser.performance.n-plus-one-requests
Request storm
60 API · 150 total

That many xhr or fetch calls, or that many requests of any kind, during one page load.

browser.performance.request-storm
Duplicate requests
3 identical calls

The same method and URL fetched three times in one load; medium at six.

browser.performance.duplicate-requests
Request waterfall
3 sequential · 600 ms

API calls that waited for each other when they could have run at once; high at five deep.

browser.performance.request-waterfall
Third-party weight
40% and 500 KB

Bytes from other origins, as a share of the page and as an absolute size; medium at 60% and 1.5 MB.

browser.performance.third-party-weight
Render-blocking scripts
3 in <head>

Synchronous external scripts in the head, each of which delays the first paint.

browser.performance.render-blocking-scripts
Uncached static assets
3 assets · 50% · 500 KB

Static files served without a cache lifetime of at least a day.

browser.performance.uncached-static-assets
Oversized images
4× displayed · 100 KB

Downloaded pixels against the pixels actually shown; medium when a megabyte is wasted.

browser.performance.oversized-images
Heavy page
3 MB

Total transferred bytes for one page load.

browser.performance.heavy-page
WebSocket chatter
400 messages · 4 reconnects

One page load that exchanges that many frames, or reopens the same socket that often.

browser.performance.websocket-chatter
Failed requests
3 same-origin failures

Requests the page made to its own origin that came back 4xx or 5xx.

browser.reliability.failed-requests
03Baselines

“Slower than before” becomes a measurement.

Load, DOM-ready and time to first byte are remembered per route. A regression is raised when a load is at least 1.5× the baseline of the last 8 samples and slower than 1,500 ms in absolute terms, so a page that went from fast to slightly less fast is not an alarm.

04Reliability

What breaks in the open.

A page that renders blank, a chunk that never loads, a script that comes back as HTML: the failures a visitor sees and an uptime check does not.

  • JavaScript errors on load
    browser.reliability.javascript-errors
  • Requests that fail in production
    browser.reliability.failed-requests
  • WebSocket reconnect loop
    browser.reliability.websocket-reconnect-loop
  • Script chunk failed to load (deploy mismatch)
    browser.reliability.chunk-load-failure
  • Script served as an HTML page
    browser.reliability.script-served-as-html
  • The 404 page itself is broken
    browser.reliability.error-page-broken-resources
05Free and paid

One page free. Twenty with a plan.

The free scan
  • One real Chromium load of the page you gave it, plus a visit to a page that does not exist.
  • Core Web Vitals, page weight, third-party share, render-blocking scripts and oversized images from that one load.
  • No crawl, no client-navigation pass, no burst against a rate limit.
Pro and above
  • Up to 20 pages per scan, routes clustered, each with a baseline.
  • The client-navigation pass, and a bounded burst of 12 requests to see whether a limit exists - only once you have verified the domain.
  • Live-site scans are free and unlimited; the queue and a five-minute cooldown are the only brake.

Measure it on your app.

A URL, an email, one real browser load. The report says which request made the page slow, and what to do about it.