Skip to content
New storyWe built Skald to count visitors. It caught an AI-key thief.
SkaldEvery visit, truly told.

Integrations · Next.js

Next.js analytics: every click inside the site, never a prefetch

Next.js analytics from the server log needs one field. A click inside a Next.js site loads no HTML page, and a prefetch looks the same in a plain log. Add the prefetch header to the log line, and Skald counts each click as a page view and no prefetch ever.

Next.js analytics is counting the visits and pages of a site built with Next.js, whose App Router opens pages inside the site with a data request (?_rsc=) instead of an HTML page. Skald reads them from your server log once it records Next.js's Next-Router-Prefetch header, and sees the AI crawlers and bots a script never does. One of the Skald integrations.

30-day free trial · no card · we set it up within one working day · free to self-host

access.log · Next.js · sample5 lines
  1. 09:14:02 198.51.100.23 "GET /" 200 rsc_prefetch=-page view
  2. 09:14:03 198.51.100.23 "GET /pricing?_rsc=1x7a" rsc_prefetch=1prefetch · not a view
  3. 09:14:21 198.51.100.23 "GET /pricing?_rsc=1x7a" rsc_prefetch=-click · page view
  4. 09:14:22 203.0.113.24 "GET /pricing" 200 "GPTBot/1.2"AI · training
  5. 09:15:40 198.51.100.23 "POST /pricing" 200 rsc_prefetch=-server action · not a view

Sample lines. One field tells a click from a prefetch.

[01] the gap

Why Next.js page views go missing from a server log

On a Next.js App Router site, a tap on a link inside the site sends no HTML request. The browser asks for /path?_rsc=<hash>. Next.js asks for the same address to prefetch links in view. Flip the switch to see one visit read without the field, then with it.

flip it

1 page view. The two clicks are lost: a plain log cannot tell them from the prefetch, so Skald counts none of them. Nothing is guessed.

one visit · 198.51.100.23 · sample4 lines
  1. 09:14:02 "GET /" 200page view
  2. 09:14:03 "GET /pricing?_rsc=1x7a" 200? not counted
  3. 09:14:21 "GET /pricing?_rsc=1x7a" 200? not counted
  4. 09:15:02 "GET /docs/setup?_rsc=9k2b" 200? not counted

[02] the fix

Add one field to the nginx log format for Next.js

Next.js marks each prefetch with the request header Next-Router-Prefetch. Log it, at the very end of the line, inside the log_format quotes. Nginx and Apache both write - when the header was not sent.

Is it safe? Yes. It adds one header's value to the log nginx already writes. It opens no port, adds no route and gives no one access, and nginx escapes an odd value as it does for every other header. sudo skald-ship-check on the server says whether the log has it.

nginx · add at the end of log_format
 rsc_prefetch=$http_next_router_prefetch
Apache · add at the end of LogFormat
 rsc_prefetch=%{Next-Router-Prefetch}i
then check and reload nginx
$ sudo nginx -t
$ sudo systemctl reload nginx

Apache: apachectl configtest, then reload. Lines written before stay as they were.

[03] how it counts

How Skald counts Next.js navigations and prefetches

The field is read on its own, after the user agent. It works beside a trailing request time, a trailing host and Skald's request id.

  1. A dash is a click

    - or nothing: a navigation, counted as a page view of its path when answered 2xx or 304.

  2. Any value is a prefetch

    1, or 2 to 4 in newer Next.js: a prefetch, never a page view.

  3. A server action is not a page

    A POST never counts as a page view.

  4. One click, one view

    An HTML page and a navigation of the same path, in one visit, 2 seconds or less apart, are one page view.

[04] where it runs

Next.js on your own server, on Caddy, or on Vercel

Where your Next.js site runs decides what the log can hold. Pick yours.

Where does it run? pick one

What Skald sees of a Next.js site, by where it runs
What Skald sees nginx or Apacheone command ships the log Caddyheaders already logged Vercellog drain, Vercel Pro Cloudflareedge records
AI crawlers and botsYesYesYesYes
The first page of each visitYesYesYesYes
Clicks inside the site, from the logYes, with the fieldYes, nothing to addNo: no request headersNo: no request headers
What you addOne field in the log formatNothingThe tag, for clicks as its own eventsThe tag, for clicks as its own events

[05] the tag and leads

The script tag and leads on a Next.js site

The log counts visits and pages. The tag adds what happens inside a page, and your server can send each lead it makes.

  1. The tag goes in the root layout

    So every page has it. It is under 6.5 KB, sends forms, bookings and clicks, and on a click inside the site or a page restored by the back button it sends its own page view. Those stay a separate road: they never add people or page views to the log's figures.

  2. Leads from an API route or a server action

    Call one function from the Node file after the lead is saved, or sign the lead yourself. It waits at most 3 seconds and never breaks the form.

[06] what it does not do

What Next.js analytics in Skald does not do

One field closes the gap on your own server. Here is what it does not reach.

What it does not do

  • Without the field, only the first page of each visit counts, and a visit from a tab left open may not count at all.
  • Vercel and Netlify drains and the Skald Worker carry no request headers, so clicks inside the site are not counted from those logs.
  • Log drains need Vercel Pro or Netlify Enterprise.
  • Old lines are not recounted: the field counts from the day you add it.
  • The tag's page views never add to the log's people or page views.

questions

Next.js analytics FAQ

Straight answers, including what Skald does not do.

Why does log analytics miss pages on a Next.js site?

A click on a link inside a Next.js site loads no new HTML page. The browser fetches only the next page's data, an address ending in ?_rsc=. Next.js fetches the same addresses for links on screen nobody clicked (a prefetch). A plain log cannot tell the two apart, so Skald counts neither, and only the first page of each visit shows.

Does a Next.js prefetch ever count as a page view in Skald?

No. Once the log records the prefetch header, any value in it (1, or 2 to 4 in newer Next.js) marks a prefetch, which never counts. A dash or nothing marks a click, counted as a page view when answered 2xx or 304. A POST, a server action, never counts.

What do I add to nginx for Next.js?

One field at the very end of the site's log_format, inside its quotes: rsc_prefetch=$http_next_router_prefetch. Then sudo nginx -t and sudo systemctl reload nginx. Apache takes rsc_prefetch=%{Next-Router-Prefetch}i at the end of its LogFormat. Caddy's JSON log already holds the header: nothing to add.

Is adding the prefetch field to my log safe?

Yes. It only adds one header's value, 1 or a dash, to the log nginx already writes. It opens no port, adds no route and gives no one access, and nginx escapes an odd value as it does for every other header.

Does it work for Next.js on Vercel?

Partly. A Vercel log drain sends the first page of each visit, with AI crawlers and bots, but carries no request headers, so a click inside the site is still not counted from the log. Log drains need Vercel Pro. The script tag's own page views can show those clicks, as a separate road.

Do I need the script tag on a Next.js site?

Not for the log's figures. The tag adds what happens inside a page: forms, bookings, clicks, and with visitor IDs on, the journey. Put it in the root layout, so every page has it. Its page views are kept apart and never added to the people or page views counted from the log.

$ tail -f access.log | skald

See what AI does with your site. Free for 30 days.

No card. We set up hosted Skald for you within one working day, or you self-host it free. Every line of your log, sorted and added up.

Start free trial Live demo