I wasn’t trying to do anything particularly clever or experimental — I was simply trying to increase my Google AdSense revenue on a few content-heavy websites I run, most of which are built with modern frameworks like Next.js and optimized heavily for SEO, performance, and long-tail traffic acquisition. increase my Google AdSense revenue These sites include small but very real projects like markkit.dev, a developer-focused tool site, and whatstype.org, a personality and test-based content website, both of which rely heavily on internal navigation and indexed subpages to generate traffic and revenue. markkit.dev whatstype.org No viral growth tricks. No redesign. No traffic spikes. Just the assumption that if ads were configured correctly, they should load consistently on every page. if ads were configured correctly, they should load consistently on every page That assumption turned out to be wrong. The First Symptom: Ads Missing on Pages That “Should” Work At first, everything looked fine. On the homepage, ads loaded exactly as expected, and on some articles and landing pages everything appeared completely normal. But on other pages — often deep-linked SEO pages reached through internal navigation on sites like markkit.dev and whatstype.org — ads simply didn’t show up at all, despite the content being indexed, receiving traffic, and meeting all AdSense policy requirements. There were no layout shifts, no obvious broken elements, and no warnings inside the AdSense dashboard itself, which made the issue even more confusing. These pages were real, production pages with users and pageviews — the kind of pages that should have been earning money. So I opened DevTools. Two Types of Errors That Didn’t Point to an Obvious Cause What I found was inconsistent, but repeatable. 1. A Console Error That Didn’t Fully Explain the Problem On some pages, the console showed: Error: no_div at adsbygoogle.js Error: no_div at adsbygoogle.js This error implies that AdSense failed to find the expected <ins class="adsbygoogle"> container, even though the element was clearly rendered in the DOM when inspected manually. <ins class="adsbygoogle"> That immediately suggested a timing or lifecycle issue, not a missing element. timing or lifecycle issue 2. Ad Requests Marked as “Canceled” On other pages, there was no JavaScript error at all. Instead, in the Network tab, several AdSense-related requests appeared briefly and then ended with a status of canceled, which usually indicates that the browser terminated the request before it could complete. canceled No 4xx errors. No 5xx errors. No retries. Just silent failure. At this point, it was clear that AdSense itself wasn’t broken — something in my application flow was interfering with it. The Common Factor: Client-Side Navigation in Next.js All of these sites are built with Next.js (App Router), and like most modern Next.js projects, internal navigation relies heavily on client-side routing via next/link, which is great for perceived performance but introduces a more complex and less predictable page lifecycle. Next.js (App Router) next/link This works beautifully for UX: Faster transitionsNo full page reloadsBetter Core Web Vitals Faster transitions No full page reloads Better Core Web Vitals But AdSense doesn’t really behave like a modern SPA-friendly script. What AdSense Still Assumes About the Web After digging through old GitHub issues, forum threads, and my own experiments, one thing became increasingly clear: Google AdSense still implicitly assumes a traditional, full page load navigation model. Google AdSense still implicitly assumes a traditional, full page load navigation model. It expects: A complete page lifecycleA stable DOM that doesn’t get torn down mid-initializationNo interrupted network requests during setup A complete page lifecycle A stable DOM that doesn’t get torn down mid-initialization No interrupted network requests during setup Client-side navigation breaks those assumptions in subtle ways — and when it does, AdSense doesn’t always fail loudly. Sometimes it just doesn’t render. The Test That Felt Wrong (But Worked) As an experiment, I replaced some internal links — especially those leading to high-traffic content pages — from next/link to plain HTML anchor tags. next/link From this: <Link href="/article/seo-tips">Read more</Link> <Link href="/article/seo-tips">Read more</Link> To this: <a href="/article/seo-tips">Read more</a> <a href="/article/seo-tips">Read more</a> This change forces a full page reload, which goes against most modern Next.js best practices and slightly slows down navigation, but it also restores a predictable and complete page lifecycle. full page reload After deploying this change: Ads began loading consistently on all pagesno_div errors disappeared entirelyPreviously canceled AdSense requests completed normallyAd impressions and revenue increased noticeably over the following days Ads began loading consistently on all pages no_div errors disappeared entirely no_div Previously canceled AdSense requests completed normally Ad impressions and revenue increased noticeably over the following days The Trade-Off I Accepted Yes, this approach has downsides: Slower navigationLess “SPA-like” behaviorSlightly worse Lighthouse performance scores Slower navigation Less “SPA-like” behavior Slightly worse Lighthouse performance scores But in practice, a site that is marginally slower and consistently monetized performs better than a fast site that silently fails to show ads. Final Thought Next.js optimizes aggressively for developer experience and performance. Google AdSense optimizes for a web model that still assumes full page loads. When those two worlds collide, the failure mode is rarely obvious — you don’t always get a clear error message. Sometimes, you just get less revenue. less revenue And that’s the hardest bug to notice.