[{"data":1,"prerenderedAt":1112},["ShallowReactive",2],{"content:\u002Fnetwork-protocol-optimization\u002Fspeculative-loading-and-prefetching\u002Favoiding-analytics-double-counting-on-prerender":3,"surroundings:\u002Fnetwork-protocol-optimization\u002Fspeculative-loading-and-prefetching\u002Favoiding-analytics-double-counting-on-prerender":1104},{"id":4,"title":5,"body":6,"description":1084,"extension":1085,"meta":1086,"navigation":1097,"path":1098,"seo":1099,"stem":1102,"__hash__":1103},"content\u002Fnetwork-protocol-optimization\u002Fspeculative-loading-and-prefetching\u002Favoiding-analytics-double-counting-on-prerender\u002Findex.md","Avoiding Analytics Double Counting on Prerender",{"type":7,"value":8,"toc":1065},"minimark",[9,13,28,48,185,190,219,223,229,235,241,247,251,256,420,424,430,434,447,608,612,615,745,749,752,756,759,763,766,827,831,834,838,841,845,871,875,881,894,904,910,914,926,935,954,966,975,984,993,1002,1011,1023,1027,1050,1055,1058,1061],[10,11,5],"h1",{"id":12},"avoiding-analytics-double-counting-on-prerender",[14,15,16,17,22,23,27],"p",{},"This guide is part of ",[18,19,21],"a",{"href":20},"\u002Fnetwork-protocol-optimization\u002Fspeculative-loading-and-prefetching\u002F","Speculative Loading & Prefetching",", within ",[18,24,26],{"href":25},"\u002Fnetwork-protocol-optimization\u002F","Network & Server Response Optimization",". When a browser prerenders a page, it runs the page's JavaScript in the background — including analytics. If the analytics script sends a page view immediately, every prerender counts as a visit, even when the user never navigates there. Page views inflate, bounce rates and session counts distort, ad impressions are recorded for ads nobody saw, and A\u002FB tests log exposures for users who never saw the variant.",[14,29,30,31,35,36,39,40,43,44,47],{},"The browser exposes everything needed to fix this: ",[32,33,34],"code",{},"document.prerendering"," is ",[32,37,38],{},"true"," while a page is being prerendered, the ",[32,41,42],{},"prerenderingchange"," event fires when it is activated, and the navigation timing entry's ",[32,45,46],{},"activationStart"," records when activation happened. Analytics, ads and experiments should wait for activation; performance metrics should be measured relative to it.",[14,49,50],{},[51,52,58,59,58,66,58,70,58,73,58,91,58,99,58,105,58,112,58,119,58,122,58,126,58,129,58,133,58,142,58,145,58,148,58,154,58,160,58,165,58,169,58,172,58,176,58,180,58],"svg",{"viewBox":53,"width":54,"role":55,"ariaLabel":56,"style":57},"0 0 760 290","100%","img","Sequence showing a prerendered page deferring its page view until activation.","height:auto;max-width:760px;display:block;margin:1.75rem auto;font-family:inherit;color:var(--fp-svg-ink)"," ",[60,61],"rect",{"className":62,"x":64,"y":64,"width":54,"height":54,"fill":65},[63],"svg-canvas","0","#ffffff",[67,68,69],"title",{},"Prerender lifecycle and analytics",[71,72,56],"desc",{},[74,75,76],"defs",{},[77,78,85],"marker",{"id":79,"viewBox":80,"refX":81,"refY":82,"markerWidth":83,"markerHeight":83,"orient":84},"faf0b5b0d5","0 0 10 10","9","5","7","auto-start-reverse",[86,87],"path",{"d":88,"fill":89,"style":90},"M0 0 L10 5 L0 10 z","currentColor","fill-opacity:0.7",[60,92],{"x":93,"y":93,"width":94,"height":95,"rx":96,"fill":97,"stroke":89,"style":98},"1","758","288","10","none","stroke-opacity:0.18",[100,101,69],"text",{"x":102,"y":103,"fill":89,"style":104},"28.0","34.0","font-size:16px;font-weight:700",[60,106],{"x":107,"y":108,"width":109,"height":103,"rx":110,"fill":89,"stroke":89,"style":111},"70.3","56.0","150.0","6","fill-opacity:0.06;stroke-opacity:0.4",[100,113,118],{"x":114,"y":115,"fill":89,"style":116,"textAnchor":117},"145.3","78.0","font-size:13px;font-weight:700","middle","Browser",[60,120],{"x":121,"y":108,"width":109,"height":103,"rx":110,"fill":89,"stroke":89,"style":111},"305.0",[100,123,125],{"x":124,"y":115,"fill":89,"style":116,"textAnchor":117},"380.0","Prerendered page",[60,127],{"x":128,"y":108,"width":109,"height":103,"rx":110,"fill":89,"stroke":89,"style":111},"539.7",[100,130,132],{"x":131,"y":115,"fill":89,"style":116,"textAnchor":117},"614.7","Analytics",[134,135],"line",{"x1":114,"y1":136,"x2":114,"y2":137,"stroke":89,"strokeWidth":138,"strokeDashArray":139,"style":141},"90.0","268.0","1.5",[140,140],"4","stroke-opacity:0.35",[134,143],{"x1":124,"y1":136,"x2":124,"y2":137,"stroke":89,"strokeWidth":138,"strokeDashArray":144,"style":141},[140,140],[134,146],{"x1":131,"y1":136,"x2":131,"y2":137,"stroke":89,"strokeWidth":138,"strokeDashArray":147,"style":141},[140,140],[134,149],{"x1":114,"y1":150,"x2":151,"y2":150,"stroke":89,"strokeWidth":138,"style":152,"markerEnd":153},"124.0","378.0","stroke-opacity:0.6","url(#faf0b5b0d5)",[100,155,159],{"x":156,"y":157,"fill":89,"style":158,"textAnchor":117},"262.7","117.0","font-size:12px","prerender (hidden)",[134,161],{"x1":124,"y1":162,"x2":163,"y2":162,"stroke":89,"strokeWidth":138,"strokeDashArray":164,"style":152,"markerEnd":153},"164.0","147.3",[82,140],[100,166,168],{"x":156,"y":167,"fill":89,"style":158,"textAnchor":117},"157.0","document.prerendering = true",[134,170],{"x1":114,"y1":171,"x2":151,"y2":171,"stroke":89,"strokeWidth":138,"style":152,"markerEnd":153},"204.0",[100,173,175],{"x":156,"y":174,"fill":89,"style":158,"textAnchor":117},"197.0","user clicks → activate",[134,177],{"x1":124,"y1":178,"x2":179,"y2":178,"stroke":89,"strokeWidth":138,"style":152,"markerEnd":153},"244.0","612.7",[100,181,184],{"x":182,"y":183,"fill":89,"style":158,"textAnchor":117},"497.3","237.0","page view on prerenderingchange",[186,187,189],"h2",{"id":188},"rapid-diagnosis","Rapid Diagnosis",[191,192,193,201,207,213],"ul",{},[194,195,196,200],"li",{},[197,198,199],"strong",{},"Compare page views with server-side navigation logs"," after enabling prerendering; a jump in page views without matching engagement suggests double counting.",[194,202,203,206],{},[197,204,205],{},"Check analytics libraries' prerender support"," (many major ones already defer).",[194,208,209,212],{},[197,210,211],{},"Test in DevTools",": trigger a prerender (Speculative loads panel), and watch network requests from the hidden page for analytics hits before activation.",[194,214,215,218],{},[197,216,217],{},"Check ad and experimentation scripts"," separately; they may not handle prerendering.",[186,220,222],{"id":221},"root-cause-analysis","Root Cause Analysis",[14,224,225,228],{},[197,226,227],{},"1. Scripts fire on load."," Analytics send hits as soon as they execute.",[14,230,231,234],{},[197,232,233],{},"2. Custom tracking."," Hand-written beacons ignore prerender state.",[14,236,237,240],{},[197,238,239],{},"3. Ads and experiments."," Impression and exposure logging on load.",[14,242,243,246],{},[197,244,245],{},"4. Performance metrics."," Measuring from navigation start instead of activation produces misleading values.",[186,248,250],{"id":249},"step-by-step-resolution","Step-by-Step Resolution",[252,253,255],"h3",{"id":254},"_1-defer-side-effects-until-activation","1. Defer side effects until activation",[257,258,263],"pre",{"className":259,"code":260,"language":261,"meta":262,"style":262},"language-javascript shiki shiki-themes github-light-high-contrast github-dark-high-contrast github-light-high-contrast","export function onActivation(fn) {\n  if (document.prerendering) {\n    document.addEventListener('prerenderingchange', () => fn(), { once: true });\n  } else {\n    fn();\n  }\n}\nonActivation(() => {\n  analytics.pageView();\n  experiments.logExposure();\n});\n\u002F\u002F trade-off: work deferred to activation runs right after the page becomes visible;\n\u002F\u002F keep it light so it does not delay the first interactions.\n","javascript","",[32,264,265,291,300,333,345,354,360,366,379,390,401,407,414],{"__ignoreMap":262},[266,267,269,273,276,280,284,288],"span",{"class":134,"line":268},1,[266,270,272],{"class":271},"sPARh","export",[266,274,275],{"class":271}," function",[266,277,279],{"class":278},"smZ65"," onActivation",[266,281,283],{"class":282},"saISM","(",[266,285,287],{"class":286},"sQw3B","fn",[266,289,290],{"class":282},") {\n",[266,292,294,297],{"class":134,"line":293},2,[266,295,296],{"class":271},"  if",[266,298,299],{"class":282}," (document.prerendering) {\n",[266,301,303,306,309,311,315,318,321,324,327,330],{"class":134,"line":302},3,[266,304,305],{"class":282},"    document.",[266,307,308],{"class":278},"addEventListener",[266,310,283],{"class":282},[266,312,314],{"class":313},"sZ8jY","'prerenderingchange'",[266,316,317],{"class":282},", () ",[266,319,320],{"class":271},"=>",[266,322,323],{"class":278}," fn",[266,325,326],{"class":282},"(), { once: ",[266,328,38],{"class":329},"sPXB4",[266,331,332],{"class":282}," });\n",[266,334,336,339,342],{"class":134,"line":335},4,[266,337,338],{"class":282},"  } ",[266,340,341],{"class":271},"else",[266,343,344],{"class":282}," {\n",[266,346,348,351],{"class":134,"line":347},5,[266,349,350],{"class":278},"    fn",[266,352,353],{"class":282},"();\n",[266,355,357],{"class":134,"line":356},6,[266,358,359],{"class":282},"  }\n",[266,361,363],{"class":134,"line":362},7,[266,364,365],{"class":282},"}\n",[266,367,369,372,375,377],{"class":134,"line":368},8,[266,370,371],{"class":278},"onActivation",[266,373,374],{"class":282},"(() ",[266,376,320],{"class":271},[266,378,344],{"class":282},[266,380,382,385,388],{"class":134,"line":381},9,[266,383,384],{"class":282},"  analytics.",[266,386,387],{"class":278},"pageView",[266,389,353],{"class":282},[266,391,393,396,399],{"class":134,"line":392},10,[266,394,395],{"class":282},"  experiments.",[266,397,398],{"class":278},"logExposure",[266,400,353],{"class":282},[266,402,404],{"class":134,"line":403},11,[266,405,406],{"class":282},"});\n",[266,408,410],{"class":134,"line":409},12,[266,411,413],{"class":412},"sjfSM","\u002F\u002F trade-off: work deferred to activation runs right after the page becomes visible;\n",[266,415,417],{"class":134,"line":416},13,[266,418,419],{"class":412},"\u002F\u002F keep it light so it does not delay the first interactions.\n",[252,421,423],{"id":422},"_2-configure-tag-managers","2. Configure tag managers",[14,425,426,427,429],{},"Use triggers that wait for activation. Some tag managers provide prerender-aware triggers; otherwise, push a custom event from ",[32,428,371],{}," and fire page view tags on that event.",[252,431,433],{"id":432},"_3-measure-performance-from-activation","3. Measure performance from activation",[14,435,436,437,439,440,443,444,446],{},"The web-vitals library reports metrics relative to ",[32,438,46],{}," for prerendered pages and sets ",[32,441,442],{},"navigationType: 'prerender'",". If you measure manually, subtract ",[32,445,46],{},".",[257,448,450],{"className":259,"code":449,"language":261,"meta":262,"style":262},"const nav = performance.getEntriesByType('navigation')[0];\nconst activation = nav.activationStart || 0;\nnew PerformanceObserver((list) => {\n  const lcp = list.getEntries().at(-1);\n  report({ lcp: Math.max(lcp.startTime - activation, 0), prerendered: activation > 0 });\n}).observe({ type: 'largest-contentful-paint', buffered: true });\n",[32,451,452,482,503,524,556,587],{"__ignoreMap":262},[266,453,454,457,460,463,466,469,471,474,477,479],{"class":134,"line":268},[266,455,456],{"class":271},"const",[266,458,459],{"class":329}," nav",[266,461,462],{"class":271}," =",[266,464,465],{"class":282}," performance.",[266,467,468],{"class":278},"getEntriesByType",[266,470,283],{"class":282},[266,472,473],{"class":313},"'navigation'",[266,475,476],{"class":282},")[",[266,478,64],{"class":329},[266,480,481],{"class":282},"];\n",[266,483,484,486,489,491,494,497,500],{"class":134,"line":293},[266,485,456],{"class":271},[266,487,488],{"class":329}," activation",[266,490,462],{"class":271},[266,492,493],{"class":282}," nav.activationStart ",[266,495,496],{"class":271},"||",[266,498,499],{"class":329}," 0",[266,501,502],{"class":282},";\n",[266,504,505,508,511,514,517,520,522],{"class":134,"line":302},[266,506,507],{"class":271},"new",[266,509,510],{"class":278}," PerformanceObserver",[266,512,513],{"class":282},"((",[266,515,516],{"class":286},"list",[266,518,519],{"class":282},") ",[266,521,320],{"class":271},[266,523,344],{"class":282},[266,525,526,529,532,534,537,540,543,546,548,551,553],{"class":134,"line":335},[266,527,528],{"class":271},"  const",[266,530,531],{"class":329}," lcp",[266,533,462],{"class":271},[266,535,536],{"class":282}," list.",[266,538,539],{"class":278},"getEntries",[266,541,542],{"class":282},"().",[266,544,545],{"class":278},"at",[266,547,283],{"class":282},[266,549,550],{"class":271},"-",[266,552,93],{"class":329},[266,554,555],{"class":282},");\n",[266,557,558,561,564,567,570,572,575,577,580,583,585],{"class":134,"line":347},[266,559,560],{"class":278},"  report",[266,562,563],{"class":282},"({ lcp: Math.",[266,565,566],{"class":278},"max",[266,568,569],{"class":282},"(lcp.startTime ",[266,571,550],{"class":271},[266,573,574],{"class":282}," activation, ",[266,576,64],{"class":329},[266,578,579],{"class":282},"), prerendered: activation ",[266,581,582],{"class":271},">",[266,584,499],{"class":329},[266,586,332],{"class":282},[266,588,589,592,595,598,601,604,606],{"class":134,"line":356},[266,590,591],{"class":282},"}).",[266,593,594],{"class":278},"observe",[266,596,597],{"class":282},"({ type: ",[266,599,600],{"class":313},"'largest-contentful-paint'",[266,602,603],{"class":282},", buffered: ",[266,605,38],{"class":329},[266,607,332],{"class":282},[252,609,611],{"id":610},"_4-segment-reports","4. Segment reports",[14,613,614],{},"Report prerendered navigations separately in performance dashboards, so improvements from speculation are visible and do not hide regressions in normal loads.",[14,616,617],{},[51,618,58,621,58,624,58,627,58,629,58,632,58,634,58,638,58,645,58,649,58,653,58,656,58,660,58,663,58,667,58,671,58,674,58,676,58,678,58,681,58,685,58,690,58,693,58,695,58,698,58,701,58,705,58,707,58,709,58,711,58,714,58,717,58,721,58,723,58,725,58,727,58,730,58,733,58,737,58,739,58,741,58,743,58],{"viewBox":619,"width":54,"role":55,"ariaLabel":620,"style":57},"0 0 760 258","Script categories and whether they should run during prerender or wait for activation.",[60,622],{"className":623,"x":64,"y":64,"width":54,"height":54,"fill":65},[63],[67,625,626],{},"What to defer until activation",[71,628,620],{},[60,630],{"x":93,"y":93,"width":94,"height":631,"rx":96,"fill":97,"stroke":89,"style":98},"256",[100,633,626],{"x":102,"y":103,"fill":89,"style":104},[60,635],{"x":102,"y":108,"width":636,"height":637,"rx":64,"fill":89,"stroke":89,"style":111},"189.5","30.0",[100,639,644],{"x":640,"y":641,"fill":89,"style":642,"textAnchor":643},"38.0","75.5","font-size:12.5px;font-weight:700","start","Script",[60,646],{"x":647,"y":108,"width":648,"height":637,"rx":64,"fill":89,"stroke":89,"style":111},"217.5","257.3",[100,650,652],{"x":651,"y":641,"fill":89,"style":642,"textAnchor":117},"346.1","During prerender",[60,654],{"x":655,"y":108,"width":648,"height":637,"rx":64,"fill":89,"stroke":89,"style":111},"474.7",[100,657,659],{"x":658,"y":641,"fill":89,"style":642,"textAnchor":117},"603.4","After activation",[60,661],{"x":102,"y":662,"width":636,"height":637,"rx":64,"fill":97,"stroke":89,"style":141},"86.0",[100,664,666],{"x":640,"y":665,"fill":89,"style":642,"textAnchor":643},"105.5","Rendering and hydration",[60,668],{"x":647,"y":662,"width":648,"height":637,"rx":64,"fill":669,"stroke":669,"style":670},"#0466c8","fill-opacity:0.14;stroke-opacity:0.9",[100,672,673],{"x":651,"y":665,"fill":89,"style":158,"textAnchor":117},"run",[60,675],{"x":655,"y":662,"width":648,"height":637,"rx":64,"fill":97,"stroke":89,"style":141},[100,677,550],{"x":658,"y":665,"fill":89,"style":158,"textAnchor":117},[60,679],{"x":102,"y":680,"width":636,"height":637,"rx":64,"fill":97,"stroke":89,"style":141},"116.0",[100,682,684],{"x":640,"y":683,"fill":89,"style":642,"textAnchor":643},"135.5","Analytics page view",[60,686],{"x":647,"y":680,"width":648,"height":637,"rx":64,"fill":687,"stroke":688,"style":689},"#ffc300","#b8860b","fill-opacity:0.24;stroke-opacity:0.9",[100,691,692],{"x":651,"y":683,"fill":89,"style":158,"textAnchor":117},"defer",[60,694],{"x":655,"y":680,"width":648,"height":637,"rx":64,"fill":669,"stroke":669,"style":670},[100,696,697],{"x":658,"y":683,"fill":89,"style":158,"textAnchor":117},"send",[60,699],{"x":102,"y":700,"width":636,"height":637,"rx":64,"fill":97,"stroke":89,"style":141},"146.0",[100,702,704],{"x":640,"y":703,"fill":89,"style":642,"textAnchor":643},"165.5","Ad requests and impressions",[60,706],{"x":647,"y":700,"width":648,"height":637,"rx":64,"fill":687,"stroke":688,"style":689},[100,708,692],{"x":651,"y":703,"fill":89,"style":158,"textAnchor":117},[60,710],{"x":655,"y":700,"width":648,"height":637,"rx":64,"fill":669,"stroke":669,"style":670},[100,712,713],{"x":658,"y":703,"fill":89,"style":158,"textAnchor":117},"load",[60,715],{"x":102,"y":716,"width":636,"height":637,"rx":64,"fill":97,"stroke":89,"style":141},"176.0",[100,718,720],{"x":640,"y":719,"fill":89,"style":642,"textAnchor":643},"195.5","A\u002FB exposure logging",[60,722],{"x":647,"y":716,"width":648,"height":637,"rx":64,"fill":687,"stroke":688,"style":689},[100,724,692],{"x":651,"y":719,"fill":89,"style":158,"textAnchor":117},[60,726],{"x":655,"y":716,"width":648,"height":637,"rx":64,"fill":669,"stroke":669,"style":670},[100,728,729],{"x":658,"y":719,"fill":89,"style":158,"textAnchor":117},"log",[60,731],{"x":102,"y":732,"width":636,"height":637,"rx":64,"fill":97,"stroke":89,"style":141},"206.0",[100,734,736],{"x":640,"y":735,"fill":89,"style":642,"textAnchor":643},"225.5","Chat widgets, autoplay media",[60,738],{"x":647,"y":732,"width":648,"height":637,"rx":64,"fill":687,"stroke":688,"style":689},[100,740,692],{"x":651,"y":735,"fill":89,"style":158,"textAnchor":117},[60,742],{"x":655,"y":732,"width":648,"height":637,"rx":64,"fill":669,"stroke":669,"style":670},[100,744,643],{"x":658,"y":735,"fill":89,"style":158,"textAnchor":117},[186,746,748],{"id":747},"verification","Verification",[14,750,751],{},"Prerender a page in DevTools and confirm no analytics, ad or experiment requests leave the hidden page until you activate it. After activation, exactly one page view should be sent. In analytics, compare page view volume with server-side navigation logs before and after enabling speculation rules: the ratio should stay stable.",[186,753,755],{"id":754},"worked-example-a-media-publisher","Worked Example: A Media Publisher",[14,757,758],{},"A publisher enabled moderate-eagerness prerendering for article links. Within a day, page views rose 23% while ad revenue per page view dropped and time-on-page fell — a signature of counting prerenders that were never activated. Their main analytics library already handled prerendering, but a custom engagement script and the ad stack fired on load. The team wrapped both in an activation helper and configured the ad library's prerender support. Page views returned to the pre-change trend (with a modest real increase from faster navigations), ad viewability metrics recovered, and LCP dashboards gained a \"prerendered\" segment showing 0.2-second LCP for those navigations.",[186,760,762],{"id":761},"experiments-and-personalisation","Experiments and Personalisation",[14,764,765],{},"A\u002FB testing during prerender has two concerns: exposure logging (defer until activation) and assignment stability (the variant chosen during prerender must match what the user sees, which it does since it is the same page instance). Server-side experiments that assign variants via cookies work naturally. Client-side experiments that modify the page should run during prerender — so the activated page already shows the variant — but log the exposure only on activation. Personalisation that depends on time-sensitive state (cart counts, notifications) may need a refresh on activation.",[14,767,768],{},[51,769,58,772,58,775,58,778,58,780,58,783,58,785,58,792,58,799,58,804,58,808,58,813,58,817,58,821,58,825,58],{"viewBox":770,"width":54,"role":55,"ariaLabel":771,"style":57},"0 0 760 163","Bar chart comparing reported page views with server-side navigations before and after fixing prerender counting.",[60,773],{"className":774,"x":64,"y":64,"width":54,"height":54,"fill":65},[63],[67,776,777],{},"Page views vs server navigations after enabling prerender",[71,779,771],{},[60,781],{"x":93,"y":93,"width":94,"height":782,"rx":96,"fill":97,"stroke":89,"style":98},"161",[100,784,777],{"x":102,"y":103,"fill":89,"style":104},[100,786,791],{"x":787,"y":788,"fill":89,"style":789,"textAnchor":790},"252.1","70.0","font-size:13px","end","Page views (scripts fire on load)",[60,793],{"x":794,"y":108,"width":795,"height":796,"rx":797,"fill":97,"stroke":89,"style":798},"264.1","403.9","19","3","fill-opacity:0.7;stroke-opacity:0.35",[100,800,803],{"x":801,"y":788,"fill":89,"style":802},"674.0","font-size:12px;font-weight:600","123%",[100,805,807],{"x":787,"y":806,"fill":89,"style":789,"textAnchor":790},"101.0","Server navigations (activated)",[60,809],{"x":794,"y":810,"width":811,"height":796,"rx":797,"fill":669,"stroke":669,"style":812},"87.0","341.5","fill-opacity:0.55;stroke-opacity:0.9",[100,814,816],{"x":815,"y":806,"fill":89,"style":802},"611.6","104%",[100,818,820],{"x":787,"y":819,"fill":89,"style":789,"textAnchor":790},"132.0","Page views (deferred to activation)",[60,822],{"x":794,"y":823,"width":811,"height":796,"rx":797,"fill":687,"stroke":688,"style":824},"118.0","fill-opacity:0.7;stroke-opacity:0.9",[100,826,816],{"x":815,"y":819,"fill":89,"style":802},[186,828,830],{"id":829},"auditing-third-party-scripts-for-prerender-safety","Auditing Third-Party Scripts for Prerender Safety",[14,832,833],{},"List every script that runs on prerenderable pages and classify it: rendering (safe to run), measurement (defer until activation), monetisation (defer), engagement (defer), and anything with user-visible side effects (defer). For each third-party script, check the vendor's documentation for prerender support, then verify in DevTools by triggering a prerender and inspecting requests from the hidden page. Scripts that cannot be made prerender-aware can be loaded inside the activation helper instead of directly in the page, which defers their execution entirely until the user sees the page.",[186,835,837],{"id":836},"reporting-metrics-for-prerendered-navigations","Reporting Metrics for Prerendered Navigations",[14,839,840],{},"Performance dashboards should treat prerendered navigations as a separate category. Their LCP values are often under 100ms, which is correct from the user's point of view but very different from normal loads. Mixing them in a single distribution makes the overall p75 improve as speculation coverage grows, which can mask regressions in non-speculated loads. Report the share of prerendered navigations, the metrics for each navigation type, and the overall distribution together, so trends in each remain visible and a regression in one is never hidden by gains in another.",[186,842,844],{"id":843},"common-mistakes","Common Mistakes",[191,846,847,853,859,865],{},[194,848,849,852],{},[197,850,851],{},"Assuming all scripts handle prerendering."," Check each analytics, ad and experiment script.",[194,854,855,858],{},[197,856,857],{},"Deferring rendering work."," Only side effects should wait; rendering should complete in the prerender.",[194,860,861,864],{},[197,862,863],{},"Measuring LCP from navigation start."," Produces large, meaningless values for prerendered pages.",[194,866,867,870],{},[197,868,869],{},"Not segmenting."," Mixed data hides both the benefit and regressions.",[186,872,874],{"id":873},"edge-cases","Edge Cases",[14,876,877,880],{},[197,878,879],{},"Pages never activated."," They are discarded without sending anything — the intended outcome.",[14,882,883,886,887,890,891,446],{},[197,884,885],{},"Back\u002Fforward cache."," Restores from bfcache are separate; page views on bfcache restore use the ",[32,888,889],{},"pageshow"," event with ",[32,892,893],{},"persisted",[14,895,896,899,900,903],{},[197,897,898],{},"Server-side analytics."," Server logs see prerender requests (with ",[32,901,902],{},"Sec-Purpose","); exclude or tag them.",[14,905,906,909],{},[197,907,908],{},"Consent management."," Consent prompts should not appear in a prerendered page before activation; defer them too.",[186,911,913],{"id":912},"faq","FAQ",[915,916,919,923],"details",{"className":917},[918],"faq-item",[920,921,922],"summary",{},"Do prerendered pages run JavaScript?",[14,924,925],{},"Yes. They load and execute scripts in the background, which is why side effects must wait for activation.",[915,927,929,932],{"className":928},[918],[920,930,931],{},"Does Google Analytics handle prerendering?",[14,933,934],{},"Major analytics libraries, including Google's, defer page views until activation. Verify your configuration and any custom tags.",[915,936,938,941],{"className":937},[918],[920,939,940],{},"How do I detect a prerendered page?",[14,942,943,944,946,947,949,950,953],{},"Check ",[32,945,34],{}," during load, listen for ",[32,948,42],{},", or check ",[32,951,952],{},"activationStart > 0"," in the navigation entry after activation.",[915,955,957,960],{"className":956},[918],[920,958,959],{},"How should LCP be measured for prerendered pages?",[14,961,962,963,965],{},"Relative to ",[32,964,46],{},". The web-vitals library does this automatically.",[915,967,969,972],{"className":968},[918],[920,970,971],{},"Do prefetches cause double counting?",[14,973,974],{},"No. Prefetch only downloads the document without running scripts.",[915,976,978,981],{"className":977},[918],[920,979,980],{},"Should ads load during prerender?",[14,982,983],{},"No. Ad requests and impressions should wait for activation, both for accuracy and to avoid wasting ad inventory.",[915,985,987,990],{"className":986},[918],[920,988,989],{},"What about server-side tracking pixels in HTML?",[14,991,992],{},"Image pixels in the HTML load during prerender. Move them behind the activation helper or identify prerender requests server-side.",[915,994,996,999],{"className":995},[918],[920,997,998],{},"Can I test this without real traffic?",[14,1000,1001],{},"Yes. Use DevTools to trigger prerenders and inspect network activity from the hidden page before activation.",[915,1003,1005,1008],{"className":1004},[918],[920,1006,1007],{},"Do session replay tools handle prerendering?",[14,1009,1010],{},"Check each vendor. Session recording should start on activation; otherwise it may record a session for a page nobody saw.",[915,1012,1014,1017],{"className":1013},[918],[920,1015,1016],{},"Will unused prerenders appear in server logs?",[14,1018,1019,1020,1022],{},"Yes. The HTML request is made, carrying a ",[32,1021,902],{}," header. Filter or tag those requests in log-based analytics.",[186,1024,1026],{"id":1025},"related","Related",[191,1028,1029,1036,1043],{},[194,1030,1031,1035],{},[18,1032,1034],{"href":1033},"\u002Fnetwork-protocol-optimization\u002Fspeculative-loading-and-prefetching\u002Fspeculation-rules-for-instant-navigations\u002F","Speculation rules for instant navigations"," — enabling prerender.",[194,1037,1038,1042],{},[18,1039,1041],{"href":1040},"\u002Fnetwork-protocol-optimization\u002Fspeculative-loading-and-prefetching\u002Fprerender-vs-prefetch-cost-trade-offs\u002F","Prerender vs prefetch cost trade-offs"," — prefetch avoids the issue entirely.",[194,1044,1045,1049],{},[18,1046,1048],{"href":1047},"\u002Fcore-web-vitals-measurement\u002F","Core Web Vitals measurement"," — measuring metrics correctly.",[1051,1052,1054],"script",{"type":1053},"application\u002Fld+json","\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"HowTo\",\n  \"name\": \"Avoiding Analytics Double Counting on Prerender\",\n  \"description\": \"How to keep analytics, ad impressions and experiment exposures accurate when pages are prerendered, and how to measure prerendered navigations correctly.\",\n  \"step\": [\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 1,\n      \"name\": \"Defer side effects until activation\",\n      \"text\": \"Defer side effects until activation\"\n    },\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 2,\n      \"name\": \"Configure tag managers\",\n      \"text\": \"Use triggers that wait for activation.\"\n    },\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 3,\n      \"name\": \"Measure performance from activation\",\n      \"text\": \"The web-vitals library reports metrics relative to activationStart for prerendered pages and sets navigationType: 'prerender'.\"\n    },\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 4,\n      \"name\": \"Segment reports\",\n      \"text\": \"Report prerendered navigations separately in performance dashboards, so improvements from speculation are visible and do not hide regressions in normal loads.\"\n    }\n  ]\n}\n",[1051,1056,1057],{"type":1053},"\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"TechArticle\",\n  \"headline\": \"Avoiding Analytics Double Counting on Prerender\",\n  \"description\": \"How to keep analytics, ad impressions and experiment exposures accurate when pages are prerendered, and how to measure prerendered navigations correctly.\",\n  \"datePublished\": \"2026-10-06\",\n  \"dateModified\": \"2026-10-06\",\n  \"author\": {\n    \"@type\": \"Organization\",\n    \"name\": \"frontend-performance.com\"\n  },\n  \"publisher\": {\n    \"@type\": \"Organization\",\n    \"name\": \"frontend-performance.com\"\n  },\n  \"mainEntityOfPage\": {\n    \"@type\": \"WebPage\",\n    \"@id\": \"https:\u002F\u002Ffrontend-performance.com\u002Fnetwork-protocol-optimization\u002Fspeculative-loading-and-prefetching\u002Favoiding-analytics-double-counting-on-prerender\u002F\"\n  }\n}\n",[1051,1059,1060],{"type":1053},"\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"BreadcrumbList\",\n  \"itemListElement\": [\n    {\n      \"@type\": \"ListItem\",\n      \"position\": 1,\n      \"name\": \"Home\",\n      \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002F\"\n    },\n    {\n      \"@type\": \"ListItem\",\n      \"position\": 2,\n      \"name\": \"Network & Server Response Optimization\",\n      \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fnetwork-protocol-optimization\u002F\"\n    },\n    {\n      \"@type\": \"ListItem\",\n      \"position\": 3,\n      \"name\": \"Speculative Loading & Prefetching\",\n      \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fnetwork-protocol-optimization\u002Fspeculative-loading-and-prefetching\u002F\"\n    },\n    {\n      \"@type\": \"ListItem\",\n      \"position\": 4,\n      \"name\": \"Avoiding Analytics Double Counting on Prerender\",\n      \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fnetwork-protocol-optimization\u002Fspeculative-loading-and-prefetching\u002Favoiding-analytics-double-counting-on-prerender\u002F\"\n    }\n  ]\n}\n",[1062,1063,1064],"style",{},"html pre.shiki code .sPARh, html code.shiki .sPARh{--shiki-default:#A0111F;--shiki-dark:#FF9492;--shiki-light:#A0111F}html pre.shiki code .smZ65, html code.shiki .smZ65{--shiki-default:#622CBC;--shiki-dark:#DBB7FF;--shiki-light:#622CBC}html pre.shiki code .saISM, html code.shiki .saISM{--shiki-default:#0E1116;--shiki-dark:#F0F3F6;--shiki-light:#0E1116}html pre.shiki code .sQw3B, html code.shiki .sQw3B{--shiki-default:#702C00;--shiki-dark:#FFB757;--shiki-light:#702C00}html pre.shiki code .sZ8jY, html code.shiki .sZ8jY{--shiki-default:#032563;--shiki-dark:#ADDCFF;--shiki-light:#032563}html pre.shiki code .sPXB4, html code.shiki .sPXB4{--shiki-default:#023B95;--shiki-dark:#91CBFF;--shiki-light:#023B95}html pre.shiki code .sjfSM, html code.shiki .sjfSM{--shiki-default:#66707B;--shiki-dark:#BDC4CC;--shiki-light:#66707B}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}",{"title":262,"searchDepth":293,"depth":293,"links":1066},[1067,1068,1069,1075,1076,1077,1078,1079,1080,1081,1082,1083],{"id":188,"depth":293,"text":189},{"id":221,"depth":293,"text":222},{"id":249,"depth":293,"text":250,"children":1070},[1071,1072,1073,1074],{"id":254,"depth":302,"text":255},{"id":422,"depth":302,"text":423},{"id":432,"depth":302,"text":433},{"id":610,"depth":302,"text":611},{"id":747,"depth":293,"text":748},{"id":754,"depth":293,"text":755},{"id":761,"depth":293,"text":762},{"id":829,"depth":293,"text":830},{"id":836,"depth":293,"text":837},{"id":843,"depth":293,"text":844},{"id":873,"depth":293,"text":874},{"id":912,"depth":293,"text":913},{"id":1025,"depth":293,"text":1026},"How to keep analytics, ad impressions and experiment exposures accurate when pages are prerendered, and how to measure prerendered navigations correctly.","md",{"slug":12,"type":1087,"breadcrumb":1088,"datePublished":1096,"dateModified":1096},"article",[1089,1092,1093,1094],{"name":1090,"url":1091},"Home","\u002F",{"name":26,"url":25},{"name":21,"url":20},{"name":5,"url":1095},"\u002Fnetwork-protocol-optimization\u002Fspeculative-loading-and-prefetching\u002Favoiding-analytics-double-counting-on-prerender\u002F","2026-10-06",true,"\u002Fnetwork-protocol-optimization\u002Fspeculative-loading-and-prefetching\u002Favoiding-analytics-double-counting-on-prerender",{"title":1100,"description":1101},"Avoiding Analytics Double Counting When Prerendering","Prerendered pages run scripts before anyone sees them. Defer page views, ads and A\u002FB exposures until activation with document.prerendering and prerenderingchange.","network-protocol-optimization\u002Fspeculative-loading-and-prefetching\u002Favoiding-analytics-double-counting-on-prerender\u002Findex","kEPKG1wm0YJZsgeZ97ax-Om9swWfvDDK2jLgdori40I",[1105,1108],{"title":21,"path":1106,"stem":1107},"\u002Fnetwork-protocol-optimization\u002Fspeculative-loading-and-prefetching","network-protocol-optimization\u002Fspeculative-loading-and-prefetching\u002Findex",{"title":1109,"path":1110,"stem":1111},"Prerender vs Prefetch Cost Trade-Offs","\u002Fnetwork-protocol-optimization\u002Fspeculative-loading-and-prefetching\u002Fprerender-vs-prefetch-cost-trade-offs","network-protocol-optimization\u002Fspeculative-loading-and-prefetching\u002Fprerender-vs-prefetch-cost-trade-offs\u002Findex",1791308076388]