[{"data":1,"prerenderedAt":542},["ShallowReactive",2],{"blog-\u002Fblog\u002Fjavascript-seo":3,"blog-siblings":440},{"id":4,"title":5,"author":6,"body":7,"category":420,"date":421,"description":422,"draft":423,"extension":424,"image":425,"meta":426,"navigation":427,"path":428,"readingTime":429,"seo":430,"stem":431,"tags":432,"updated":438,"__hash__":439},"blog\u002Fblog\u002Fjavascript-seo.md","JavaScript SEO: Google renders your pages, ChatGPT may not","devin",{"type":8,"value":9,"toc":396},"minimark",[10,14,17,20,25,28,128,139,154,157,161,170,179,183,186,218,221,225,244,250,254,275,278,281,285,290,293,296,300,308,312,315,319,326,334,338,341,348,352,364,368,372,375,379,382,386,389,393],[11,12,13],"p",{},"JavaScript SEO is making sure crawlers can read the content your scripts build in the browser. ChatGPT, Claude and Perplexity may not: in tests published in 2026, all three answered from a page's raw HTML and ignored what its scripts added. Google is the easier case. Googlebot queues pages for rendering and indexes the result.",[11,15,16],{},"A modern framework doesn't settle it. Of the 453 YC startup homepages I could read on 12 October 2026, 25 (5.5%, 3.8–8.0%) were empty until their scripts ran. Thirteen were single-page apps built with Vite, and four were built with Next.js, which renders on the server by default.",[18,19],"cta-inline",{},[21,22,24],"h2",{"id":23},"javascript-seo-for-ai-crawlers-who-renders-what","JavaScript SEO for AI crawlers: who renders what",[11,26,27],{},"Rendering means loading a page in a browser engine and running its scripts before reading it. Google does it for Search. Most of the fetchers behind AI answers don't, in the tests and logs I found.",[29,30,31,47],"table",{},[32,33,34],"thead",{},[35,36,37,41,44],"tr",{},[38,39,40],"th",{},"Who fetches",[38,42,43],{},"What for",[38,45,46],{},"Runs your JavaScript?",[48,49,50,62,73,84,95,106,117],"tbody",{},[35,51,52,56,59],{},[53,54,55],"td",{},"Googlebot",[53,57,58],{},"Google Search, including AI Overviews and AI Mode",[53,60,61],{},"Yes, after a queue",[35,63,64,67,70],{},[53,65,66],{},"Bingbot",[53,68,69],{},"Bing search",[53,71,72],{},"Sometimes",[35,74,75,78,81],{},[53,76,77],{},"ChatGPT-User, Claude-User",[53,79,80],{},"A page someone's question needs right now",[53,82,83],{},"No, in every 2026 test I found",[35,85,86,89,92],{},[53,87,88],{},"Gemini, reading a link you paste",[53,90,91],{},"That one answer",[53,93,94],{},"No, in four of five tests",[35,96,97,100,103],{},[53,98,99],{},"OAI-SearchBot, GPTBot",[53,101,102],{},"ChatGPT's search index; OpenAI's model training",[53,104,105],{},"Mostly not; on some sites since mid-2026",[35,107,108,111,114],{},[53,109,110],{},"PerplexityBot, ClaudeBot",[53,112,113],{},"Perplexity's search index; Anthropic's model training",[53,115,116],{},"No, in the logs I found",[35,118,119,122,125],{},[53,120,121],{},"Browser agents: ChatGPT agent, Claude in Chrome",[53,123,124],{},"A task someone handed them",[53,126,127],{},"Yes, they drive a real browser",[11,129,130,131,138],{},"The cleanest test is Search Engine World's, written up by Andre Alpar in June 2026. Its team gave ",[132,133,137],"a",{"href":134,"rel":135},"https:\u002F\u002Fwww.searchengineworld.com\u002Fdo-ai-assistants-actually-render-your-javascript-when-grounding-we-put-it-to-the-test",[136],"nofollow","12 AI assistants a page"," whose raw HTML held a decoy number, while a script fetched the real one. ChatGPT, Claude, Gemini, Perplexity, Meta AI and Copilot all answered with the decoy. In Alpar's words, \"the entire US top tier reads raw HTML only.\" Five ran the script and reported the real number: DeepSeek, ERNIE, Qwen, Kimi and Mistral. Grok ran it on one node and still answered with the decoy.",[11,140,141,142,147,148,153],{},"Server logs agree. A ",[132,143,146],{"href":144,"rel":145},"https:\u002F\u002Fforgeaibotlog.com\u002Farticles\u002Fcan-ai-crawlers-execute-javascript\u002F",[136],"30-day log of one news site",", published in September 2026 by a company that sells a bot-log plugin, found \"not one script in 37,045 ChatGPT-User page reads\", and none in 10,028 Claude-User reads. Those requests were matched by user agent, not checked against the companies' IP ranges. Vercel and MERJ's ",[132,149,152],{"href":150,"rel":151},"https:\u002F\u002Fvercel.com\u002Fblog\u002Fthe-rise-of-the-ai-crawler",[136],"2024 study"," had found the same for the crawlers of OpenAI, Anthropic and Perplexity.",[11,155,156],{},"Google's own JavaScript guide says server-side rendering or prerendering \"is still a great idea\", partly because \"not all bots can run JavaScript\".",[21,158,160],{"id":159},"openais-search-crawler-may-be-starting-to-render","OpenAI's search crawler may be starting to render",[11,162,163,164,169],{},"The exception is recent and small. A developer who checks crawler requests against OpenAI's published IP ranges ",[132,165,168],{"href":166,"rel":167},"https:\u002F\u002Fhisashi.space\u002Fblog\u002Fchatgpt-crawler-js-rendering\u002F",[136],"saw OAI-SearchBot and GPTBot start running JavaScript"," on their own Next.js sites on 25 September 2026. Three days later, on one of those sites, 84% of OAI-SearchBot's requests and 97% of GPTBot's were the follow-up requests a Next.js page makes only when its scripts run. The developer's reading: \"It looks like rendering is only switched on for some sites.\" An IP-checked log from July 2026 caught OAI-SearchBot doing it on 19 of 57 requests over eight days, and the 30-day news-site log saw it load one page in 1,738 the way a browser does.",[11,171,172,173,178],{},"OpenAI hasn't said anything about JavaScript on ",[132,174,177],{"href":175,"rel":176},"https:\u002F\u002Fdevelopers.openai.com\u002Fapi\u002Fdocs\u002Fbots",[136],"the page that documents its bots",", either way. So a page may get rendered on its way into ChatGPT's search index, and I wouldn't plan around it. The fetch ChatGPT makes while it answers is different: in every 2026 test I found, it read only the raw HTML.",[21,180,182],{"id":181},"what-a-non-rendering-crawler-gets-from-an-empty-page","What a non-rendering crawler gets from an empty page",[11,184,185],{},"I re-fetched the 17 homepages my scan had flagged, on 13 October, and looked at what they do carry:",[187,188,189,202,208],"ul",{},[190,191,192,196,197,201],"li",{},[193,194,195],"strong",{},"A title."," All 17 had a ",[198,199,200],"code",{},"\u003Ctitle>"," naming the company.",[190,203,204,207],{},[193,205,206],{},"Sometimes a description."," 12 had a meta description.",[190,209,210,213,214,217],{},[193,211,212],{},"No way in."," 16 had no link to any other page on the site. The 17th had links only inside a ",[198,215,216],{},"\u003Cnoscript>"," block, the one place a crawler that skips scripts would find them.",[11,219,220],{},"So a crawler that skips scripts learns your name and, if you wrote one, a one-line description. On 16 of the 17 it can't follow the homepage to your pricing, docs or blog, though it may still reach them through your sitemap or links from other sites.",[21,222,224],{"id":223},"nextjs-doesnt-guarantee-server-rendering","Next.js doesn't guarantee server rendering",[11,226,227,228,231,232,235,236,239,240,243],{},"Four of the 25 empty homepages were Next.js App Router sites that sent an empty page body. Two of them carried Next's own ",[198,229,230],{},"BAILOUT_TO_CLIENT_SIDE_RENDERING"," marker. Next.js leaves part of a page, or all of it, to the browser when a component can't render on the server. A component loaded with ",[198,233,234],{},"ssr: false"," is one documented cause: it stays empty in the HTML the server sends. Another is reading the URL's query string with ",[198,237,238],{},"useSearchParams()"," outside a ",[198,241,242],{},"\u003CSuspense>"," boundary, which Next.js says will \"opt the entire page into client-side rendering\". Current versions stop the build when that happens on a static page, so that one is harder to ship by accident.",[11,245,246,247,249],{},"In your browser the page looks complete, and Google renders it anyway, so it's easy to miss. Search the page source for ",[198,248,230],{},": a match means Next.js left at least part of the page to the browser. Two of the four empty Next.js homepages had no marker, though, so run the checks below either way.",[21,251,253],{"id":252},"single-page-application-spa-seo","Single page application (SPA) SEO",[11,255,256,257,260,261,264,265,270,271,274],{},"A single page application sends one HTML shell for every URL and builds each page in the browser. Google renders the shell and indexes what the scripts produce, but it only discovers links that are ",[198,258,259],{},"\u003Ca>"," elements with an ",[198,262,263],{},"href",". An app that answers every URL with a 200 also needs another way to mark missing pages, and Google's guide ",[132,266,269],{"href":267,"rel":268},"https:\u002F\u002Fdevelopers.google.com\u002Fsearch\u002Fdocs\u002Fcrawling-indexing\u002Fjavascript\u002Fjavascript-seo-basics",[136],"covers two",": redirect to a URL that returns a 404, or add a ",[198,272,273],{},"noindex"," tag.",[11,276,277],{},"For a crawler that skips scripts, every URL of a typical SPA is the same page: the same title, the same description and no content. All 17 homepages above were shells of this kind: 13 built with Vite, 3 with Create React App and 1 with Nuxt.",[11,279,280],{},"The fix doesn't have to touch the app itself. For most SaaS sites, the pages you want in AI answers are the homepage, pricing, docs and blog. Those can be static or server-rendered HTML while the logged-in app stays an SPA. If everything shares one codebase, a prerender step at build time can write an HTML file for each public route.",[21,282,284],{"id":283},"how-to-check-your-own-pages","How to check your own pages",[286,287,289],"h3",{"id":288},"turn-javascript-off-in-chrome","Turn JavaScript off in Chrome",[11,291,292],{},"Open the page, press F12 (Cmd+Option+I on a Mac) to open DevTools, then Ctrl+Shift+P (Cmd+Shift+P on a Mac), type \"Disable JavaScript\" and press Enter. Reload. What's left is close to what ChatGPT's fetch reads, minus some styling. Text in a closed tab or accordion may not show here but can still be in the HTML, so open the page source before you count it missing. The setting lasts only while DevTools is open.",[11,294,295],{},"Do it on more than the homepage. Pricing, a docs page and a blog post often come from different templates, and one of them may be the empty one.",[286,297,299],{"id":298},"count-the-text-your-homepage-sends","Count the text your homepage sends",[11,301,302,303,307],{},"Shruwd, the tool we are building, has a ",[132,304,306],{"href":305},"\u002Fscan","free scan"," that fetches your homepage without running scripts and reports how many characters of text came back. If your homepage is the blank kind described above, that number will be at or near zero.",[286,309,311],{"id":310},"dont-use-googles-tools-for-this","Don't use Google's tools for this",[11,313,314],{},"Google's URL Inspection tool and Rich Results Test show the page after Googlebot has rendered it. Google's JavaScript troubleshooting guide sends you to them \"to test how Google crawls and renders a URL\". That answers whether Google sees your content, which is a different question. The Mobile-Friendly Test that older guides still suggest was retired in December 2023.",[286,316,318],{"id":317},"read-your-logs-for-signs-of-rendering","Read your logs for signs of rendering",[11,320,321,322,325],{},"A crawler that runs your scripts also makes the requests only those scripts trigger: the API calls your page makes, and on Next.js App Router, requests carrying a ",[198,323,324],{},"?_rsc="," parameter. That's the signal the developer above used. Downloads of scripts, fonts or images don't prove it on their own. Vercel's 2024 data had ChatGPT's crawler downloading scripts on 11.50% of its requests and Claude's on 23.84%, and running none of them.",[11,327,328,329,333],{},"Match crawler requests against the companies' published IP ranges before you count them, since anyone can send a crawler's user agent. The ",[132,330,332],{"href":331},"\u002Fai-crawlers#verification","AI crawler list"," shows how to check an address. If a verified crawler makes those script-only requests, that's strong evidence it renders your pages. If you find none over a few weeks, plan as if it reads only the HTML your server sends.",[21,335,337],{"id":336},"how-to-fix-it","How to fix it",[11,339,340],{},"Have your server send pages with the content already in them, through server-side rendering or by generating static HTML at build time. Google recommends \"server-side rendering, static rendering, or hydration\", and those work whether or not a crawler runs scripts.",[11,342,343,344,347],{},"Serving prerendered HTML only to crawlers, which Google calls dynamic rendering, is \"a workaround and not a recommended solution\" in Google's words. If you already run one, it works only for the user agents on its list, so check that list. The default list in ",[198,345,346],{},"prerender-node",", Prerender's Node middleware, has no entry that matches Claude-User or Claude-SearchBot.",[21,349,351],{"id":350},"how-i-checked","How I checked",[11,353,354,355,359,360,363],{},"The 500 homepages are the ",[132,356,358],{"href":357},"\u002Fblog\u002Fyc-startups-ai-crawlers","YC scan",": B2B startups with up to 20 people from YC's 2021 to 2024 batches, drawn at random, each fetched once as a normal browser on 12 October 2026. The scan's rule calls a page JavaScript-only if it has fewer than 500 characters of text plus an empty app container with a known id, such as ",[198,361,362],{},"#root",". It found 17. Ten more homepages had zero characters of text but no container the rule recognises. I re-fetched those 10 on 13 October: eight were apps that build the page in the browser without the container the rule looks for, one was a redirect, and one now carries text. That makes 25 of 453 homepages with no body text before scripts run. Each site was fetched as a browser, so a site that serves prerendered HTML to known crawlers would look different to them.",[21,365,367],{"id":366},"frequently-asked-questions","Frequently asked questions",[286,369,371],{"id":370},"can-chatgpt-read-javascript","Can ChatGPT read JavaScript?",[11,373,374],{},"Mostly not. When ChatGPT fetched a page to answer a question, it read the raw HTML and ran none of the scripts in every 2026 test I found, including a decoy test of 12 AI assistants and a log of 37,045 ChatGPT-User page reads. OpenAI's search crawler, OAI-SearchBot, has rendered pages on some sites since at least July 2026. ChatGPT's agent mode drives a real browser, so it would run JavaScript, but it's a different requester and its requests carry a Chrome user agent.",[286,376,378],{"id":377},"are-single-page-applications-bad-for-seo","Are single page applications bad for SEO?",[11,380,381],{},"Not for Google, which renders them, though it needs links it can follow and a way to mark missing pages. For ChatGPT, Claude and Perplexity, an SPA that sends an empty shell is a problem, because when they fetch a page to answer, they mostly read only the shell. Serve your public pages as server-rendered or prerendered HTML and keep the app as it is.",[286,383,385],{"id":384},"do-ai-overviews-read-javascript-content","Do AI Overviews read JavaScript content?",[11,387,388],{},"They draw on Google's index, which Googlebot builds after rendering pages, so content your scripts add can reach them. Gemini is different when you paste a link into it: in four of five tests published since late 2025, its live fetch read only the raw HTML.",[286,390,392],{"id":391},"is-dynamic-rendering-still-ok","Is dynamic rendering still OK?",[11,394,395],{},"Google calls it \"a workaround and not a recommended solution\" and recommends server-side rendering, static rendering or hydration instead. If you keep it, check that its list of user agents includes the AI fetchers, because older default lists were written for search engines and social previews.",{"title":397,"searchDepth":398,"depth":398,"links":399},"",3,[400,402,403,404,405,406,412,413,414],{"id":23,"depth":401,"text":24},2,{"id":159,"depth":401,"text":160},{"id":181,"depth":401,"text":182},{"id":223,"depth":401,"text":224},{"id":252,"depth":401,"text":253},{"id":283,"depth":401,"text":284,"children":407},[408,409,410,411],{"id":288,"depth":398,"text":289},{"id":298,"depth":398,"text":299},{"id":310,"depth":398,"text":311},{"id":317,"depth":398,"text":318},{"id":336,"depth":401,"text":337},{"id":350,"depth":401,"text":351},{"id":366,"depth":401,"text":367,"children":415},[416,417,418,419],{"id":370,"depth":398,"text":371},{"id":377,"depth":398,"text":378},{"id":384,"depth":398,"text":385},{"id":391,"depth":398,"text":392},"Diagnostics","2026-10-13","Googlebot runs your JavaScript, and most AI fetchers don't. Who renders what in 2026, how to check your own pages, and how to fix them.",false,"md","\u002Fimgs\u002Fjavascript-seo.jpg",{},true,"\u002Fblog\u002Fjavascript-seo",9,{"title":5,"description":422},"blog\u002Fjavascript-seo",[433,434,435,436,437],"JavaScript SEO","SPA","AI crawlers","ChatGPT","server-side rendering",null,"rsjGQpCOtRXU3XzB9Wq5E7ESaWqp3xBtXN0JjM67bb0",[441,454,465,477,486,497,506,508,517,524,533],{"path":442,"title":443,"description":444,"date":445,"category":446,"image":447,"tags":448,"draft":423,"readingTime":453},"\u002Fblog\u002Fai-impressions-traffic-drop","AI impressions rising, traffic falling: what to check","Why Search Console can show AI impressions rising while organic clicks fall, what an AI impression counts, and how to check whether the answer named you.","2026-09-30","Measurement","\u002Fimgs\u002Fai-impressions-traffic-drop.jpg",[449,450,451,452],"Search Console","AI Overviews","AI impressions","organic traffic",7,{"path":455,"title":456,"description":457,"date":458,"category":446,"image":459,"tags":460,"draft":423,"readingTime":429},"\u002Fblog\u002Fai-visibility","AI visibility: how to measure it, and why one number lies","AI visibility is a rate across repeated runs, so one score from one check tells you little. What to count, and how to tell a real change from noise.","2026-09-18","\u002Fimgs\u002Fai-visibility.jpg",[461,462,463,464],"AI visibility","measurement","share of voice","GEO",{"path":466,"title":467,"description":468,"date":469,"category":470,"image":471,"tags":472,"draft":423,"readingTime":476},"\u002Fblog\u002Fbest-ai-visibility-tools","Best AI visibility tools in 2026, and the catch with each","Nine AI visibility tools compared on price, AI engines, how often they run and how they collect answers, from each vendor's own pages, checked September 2026.","2026-09-21","Tools","\u002Fimgs\u002Fbest-ai-visibility-tools.jpg",[461,473,474,475],"AEO tools","GEO tools","tools",11,{"path":478,"title":479,"description":480,"date":469,"category":420,"image":481,"tags":482,"draft":423,"readingTime":429},"\u002Fblog\u002Fcloudflare-block-ai-bots","Cloudflare Block AI Bots: what changed, and what to check","Cloudflare replaced its Block AI Bots toggle on 15 September 2026. What the new settings do to Googlebot and ChatGPT, and how to see what it turns away.","\u002Fimgs\u002Fcloudflare-block-ai-bots.jpg",[483,435,484,485],"Cloudflare","Bot Fight Mode","OAI-SearchBot",{"path":487,"title":488,"description":489,"date":458,"category":490,"image":491,"tags":492,"draft":423,"readingTime":496},"\u002Fblog\u002Fgeo-vs-seo","GEO vs SEO: what changed, what didn't, what to do first","How GEO differs from SEO, which GEO advice has evidence behind it, and two checks you can run on your own site today.","Guides","\u002Fimgs\u002Fgeo-vs-seo.jpg",[464,493,494,495],"SEO","AEO","AI search",13,{"path":498,"title":499,"description":500,"date":501,"category":490,"image":502,"tags":503,"draft":423,"readingTime":505},"\u002Fblog\u002Fhow-to-rank-in-chatgpt","How to rank in ChatGPT: what decides which sites it cites","Which OpenAI crawler to let in, where ChatGPT searches, what it can read on your page, and how to check your server logs for its visits.","2026-09-19","\u002Fimgs\u002Fhow-to-rank-in-chatgpt.jpg",[504,485,464,495],"ChatGPT SEO",8,{"path":428,"title":5,"description":422,"date":421,"category":420,"image":425,"tags":507,"draft":423,"readingTime":429},[433,434,435,436,437],{"path":509,"title":510,"description":511,"date":458,"category":490,"image":512,"tags":513,"draft":423,"readingTime":476},"\u002Fblog\u002Fllm-seo","LLM SEO: five levers with evidence, and two without","Five LLM SEO levers with evidence behind them, graded by strength, and the reason schema markup and llms.txt aren't among them.","\u002Fimgs\u002Fllm-seo.jpg",[514,464,495,515,516],"LLM SEO","llms.txt","schema",{"path":518,"title":519,"description":520,"date":445,"category":446,"image":521,"tags":522,"draft":423,"readingTime":496},"\u002Fblog\u002Fprompt-tracking","Prompt tracking: which prompts to track, with 32 examples","Which prompts to track to see whether ChatGPT and Google's AI Overviews name you: eight kinds, 32 examples across four businesses, and how many you need.","\u002Fimgs\u002Fprompt-tracking.jpg",[523,436,461,495],"Prompt tracking",{"path":525,"title":526,"description":527,"date":528,"category":420,"image":529,"tags":530,"draft":423,"readingTime":532},"\u002Fblog\u002Fwhy-chatgpt-recommends-your-competitor","Why ChatGPT recommends your competitor: 10 reasons","Ten reasons ChatGPT names a competitor instead of you, in the order to check them, each with a check you can run, the fix, and how strong the evidence is.","2026-09-26","\u002Fimgs\u002Fwhy-chatgpt-recommends-your-competitor.jpg",[436,461,531,495],"brand mentions",15,{"path":357,"title":534,"description":535,"date":421,"category":420,"image":536,"tags":537,"draft":423,"readingTime":541},"I scanned 500 YC startups to see if AI search can read them","I checked the robots.txt and homepage HTML of 500 YC B2B startups. 3 of 471 block an AI answer crawler. Empty JavaScript homepages are more common. What to check on your site.","\u002Fimgs\u002Fyc-startups-ai-crawlers.jpg",[435,538,539,485,540],"robots.txt","GPTBot","JavaScript",10,1791911305829]