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
- 09:14:02 198.51.100.23 "GET /" 200 rsc_prefetch=-page view
- 09:14:03 198.51.100.23 "GET /pricing?_rsc=1x7a" rsc_prefetch=1prefetch · not a view
- 09:14:21 198.51.100.23 "GET /pricing?_rsc=1x7a" rsc_prefetch=-click · page view
- 09:14:22 203.0.113.24 "GET /pricing" 200 "GPTBot/1.2"AI · training
- 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.
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.
- 09:14:02 "GET /" 200page view
- 09:14:03 "GET /pricing?_rsc=1x7a" 200? not counted
- 09:14:21 "GET /pricing?_rsc=1x7a" 200? not counted
- 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.
rsc_prefetch=$http_next_router_prefetch
rsc_prefetch=%{Next-Router-Prefetch}i
$ 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.
A dash is a click
-or nothing: a navigation, counted as a page view of its path when answered 2xx or 304.Any value is a prefetch
1, or2to4in newer Next.js: a prefetch, never a page view.A server action is not a page
A POST never counts as a page view.
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 | nginx or Apacheone command ships the log | Caddyheaders already logged | Vercellog drain, Vercel Pro | Cloudflareedge records |
|---|---|---|---|---|
| AI crawlers and bots | Yes | Yes | Yes | Yes |
| The first page of each visit | Yes | Yes | Yes | Yes |
| Clicks inside the site, from the log | Yes, with the field | Yes, nothing to add | No: no request headers | No: no request headers |
| What you add | One field in the log format | Nothing | The tag, for clicks as its own events | The 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.
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.
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.
keep reading
More on Next.js analytics
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.