← All posts· ~7 min

Can AI Crawlers Run JavaScript? 7 Days of Our Server Log

In 7 days GPTBot fetched 157 of our pages but our script only once; Meta-ExternalAgent and Amazonbot never fetched it. Googlebot did. Data, method, limits.

By Long Nguyen — CTO, ERPFit

Can AI Crawlers Run JavaScript? 7 Days of Our Server Log

Short answer: mostly no. In 7 days of citestat.com's server log (28 October – 4 November 2026), GPTBot fetched 157 pages but our page script only once; ClaudeBot fetched 66 pages and the script once; Meta-ExternalAgent (201 pages) and Amazonbot (98 pages) never fetched it. Googlebot, the control, came back for the script 5 times, 4 of them just 4–6 seconds after a page.

That matches what Vercel and MERJ measured in 2024: the crawlers behind ChatGPT and Claude may download a script, but they do not render the page. Text you want cited has to be in the first HTML response.

Key Takeaways

  • Every citestat.com page loads exactly one external script. A client that renders the page must fetch it (unless it has a cached copy); a client that only reads HTML never asks for it.
  • 7 days, about 1,200 requests from crawlers that declare a token (pages, robots.txt, sitemaps, llms.txt and .md files, images and the script): GPTBot 157 pages / 1 script, ClaudeBot 66 / 1, Meta-ExternalAgent 201 / 0, Amazonbot 98 / 0.
  • Googlebot (217 pages / 5 scripts) fetched the script 4–6 seconds after a page in 4 of 5 cases: the signature of a render step. Applebot (12 / 5) fetched it too, but minutes to hours after a page: the log cannot tell a render from a plain fetch there.
  • GPTBot's single fetch sat in a run of pages at about one URL per second, the way it fetched images: a URL found in the HTML, not a render.

How did we measure it?

Every page on citestat.com loads exactly one external script: /vendor/tailwindcss-3.4.17.js, the library that styles the page. A client that renders the page, meaning it runs the page in a browser engine, has to fetch that file unless it has a cached copy. A client that only reads HTML has no reason to. So for each crawler we count two things: the pages it fetched and the times it fetched the script.

Since 02:26 UTC on 28 October 2026, our web server writes one line for every request whose user agent carries a known crawler token: time, path, status code, the token, and the class of Accept header. No IP address and no full user-agent string are kept.

Our own test requests are removed from the counts. The window in this post is exactly 7 days, to 02:26 UTC on 4 November 2026.

What did each crawler fetch?

Four AI crawlers fetched 522 pages and the script twice; Googlebot and Applebot fetched it 10 times.

Declared tokenPagesScript fetchesImagesRenders?
Googlebot (control)21750Yes: 4 of 5 times, 4–6 s after a page
Applebot12520Possibly: script minutes to hours after a page
Meta-ExternalAgent20101No sign of it
GPTBot157112No sign of it
Amazonbot98021No sign of it
ClaudeBot66117No sign of it
OAI-SearchBot, PerplexityBot, Claude-User5, 3, 101, 3, 0Too few to say

Source: citestat.com server log, 28 October 02:26 – 4 November 2026 02:26 UTC, by self-declared user-agent token. bingbot only read robots.txt (9 times) and fetched no page in this window, so it cannot serve as a control here.

Raw log excerpt (UTC time, path, token; the log keeps no IP):

2026-10-30T11:53:54Z GET /docs/api                      agent=GPTBot
2026-10-30T11:53:55Z GET /vendor/tailwindcss-3.4.17.js  agent=GPTBot
2026-10-30T11:53:56Z GET /wiki/google-fonts             agent=GPTBot
2026-10-30T11:53:56Z GET /favicon.svg                   agent=GPTBot
2026-10-30T11:53:57Z GET /dashboard/settings            agent=GPTBot
2026-11-01T09:14:35Z GET /en/wiki/citability            agent=Googlebot
2026-11-01T09:14:39Z GET /vendor/tailwindcss-3.4.17.js  agent=Googlebot
2026-11-02T22:36:54Z GET /en/wiki/indexnow              agent=Googlebot
2026-11-02T22:36:59Z GET /vendor/tailwindcss-3.4.17.js  agent=Googlebot
2026-10-28T10:45:05Z GET /robots.txt                    agent=ClaudeBot
2026-10-28T10:45:44Z GET /vendor/tailwindcss-3.4.17.js  agent=ClaudeBot
2026-11-03T16:47:13Z GET /en/wiki/mcp-server-card       agent=Applebot
2026-11-03T16:59:55Z GET /vendor/tailwindcss-3.4.17.js  agent=Applebot

Meta-ExternalAgent and Amazonbot fetched 299 pages between them and never asked for the script. A client that never loads a page's script cannot have run it on our pages, unless it carries a copy from somewhere else, which a log cannot see.

Googlebot as the control: a JavaScript render in the log

Google renders JavaScript, so Googlebot tests the method itself. If Googlebot never fetched the script either, the measurement would be broken. It did, 5 times, and 4 of them follow one pattern. On 1 November Googlebot fetched /en/wiki/citability at 09:14:35 UTC and the script at 09:14:39. On 2 November it was /en/wiki/indexnow at 22:36:54 and the script at 22:36:59. Page first, script a few seconds later: the render step following the crawl. In those 4 the gap was 4–6 seconds with no other Googlebot URL in between; the fifth (31 October) came 18 minutes after the nearest page. GPTBot's fetch below came 1 second after a page, but in the middle of a run of other URLs.

The rule for reading a log: a render step fetches the script seconds after one page, with no other URL in between; a crawler that only collects URLs fetches it inside a run of other URLs, at the same pace.

5 of 217 is not a render rate. Google's JavaScript SEO documentation says Googlebot "caches aggressively in order to reduce network requests and resource usage" and that the renderer "may ignore caching headers". Our script is also sent with Cache-Control: immutable for a year.

So this post gives counts and timings, never a percentage. Applebot fetched the script too, 5 times for 12 pages, but minutes to hours after a page rather than seconds. So our log does not prove that Applebot renders; for that we rely on Vercel's finding that AppleBot renders JavaScript.

Why did GPTBot and ClaudeBot fetch the script once?

For GPTBot, the fetch was one more URL in a crawl run, not a render. On 30 October GPTBot went through our site at about one URL per second:

  • 11:53:54 /docs/api
  • 11:53:55 the script
  • 11:53:56 /wiki/google-fonts and /favicon.svg
  • 11:53:57 /dashboard/settings

The script came 1 second after /docs/api, but at the same pace as the next page and the favicon: one more URL on the list, like the 12 images it also fetched (9 blog cover images, the favicon and the two VietQR codes on our pricing page).

ClaudeBot fetched the script on 28 October, 39 seconds after robots.txt and after no page, then blog cover images over the next few minutes: consistent with the same reading.

Fetching a file is not running it. Vercel and MERJ's study (December 2024) saw the same at scale: "ChatGPT and Claude crawlers do fetch JavaScript files (ChatGPT: 11.50%, Claude: 23.84% of requests), they don't execute them."

One fetch in 7 days, between pages, fits a crawler collecting URLs from HTML, not a browser. Our log alone cannot rule out a renderer with a cache; what makes it unlikely is the timing, together with Vercel's study and a 2026 controlled test of live fetches by twelve AI assistants.

Limits of this measurement

  • Tokens are self-declared. Anyone can send the user agent "GPTBot". We did not verify IP addresses.
  • One site, one script, 7 days. About 1,200 crawler requests. Enough to see a pattern, not to measure a rate for the whole web.
  • Fetching is not running. A log shows who downloaded a file, not who executed it.
  • Caching. A renderer can reuse a stored copy; Googlebot does exactly that.
  • Rare visitors say nothing yet. PerplexityBot fetched 3 pages, OAI-SearchBot 5, Claude-User 1.

Put the text in the first HTML response

The same as in our earlier post on SSR and pre-rendering: put the main text, title, meta description, canonical, H1, JSON-LD and real links in the first HTML response, with server-side rendering, static generation or pre-rendering. Interactive parts can stay client-side. The full list is in JavaScript SEO. Google-Extended is a robots.txt token, not a separate crawler, so it never shows up as a fetch in a log; Google's AI features use pages Googlebot has crawled and rendered.

AI crawlers read what is available as text. In the same log, Meta-ExternalAgent read llms.txt and fetched the 13 .md links in it within two minutes. No crawler sent Accept: text/markdown; see markdown negotiation and markdown for AI agents. For which bots to let in, see AI crawlers and robots.txt and the wiki page AI crawlers.

How can you check your own server log?

  1. Pick one script every page loads, for example your main bundle /assets/app.js.
  2. Count pages and script fetches per bot. With an nginx-style log, the first line counts script fetches, the second pages (roughly):
    grep GPTBot access.log | grep -c '/assets/app.js'
    grep GPTBot access.log | grep -vcE '\.(js|css|png|jpe?g|webp|svg|ico|xml|txt)'
  3. Look at the timing. Google's renderer fetched the script seconds after the page; other renderers (Applebot here) can take minutes. A crawler collecting URLs fetches it between other pages, at the same pace.
  4. Verify the bot. OpenAI publishes GPTBot's IP ranges; Google explains how to verify Googlebot with reverse DNS. Until you verify, call the counts "declared".

How does CiteStat check this?

CiteStat fetches your page's raw HTML without running JavaScript, the view the crawlers in the table above get, and counts the visible words. The rules engine warns when that HTML is an app shell (an empty #root, #app or #__next mount node) with fewer than 50 words, suggests server-side rendering below 100 words, and checks for crawlable <a href> links and the robots.txt rules for 10 AI crawler tokens. CiteStat does not read your server log; for that, follow the four steps above. Scan one of your pages for free to see how many words GPTBot gets in its raw HTML.

The free plan includes 10 scans a day. For several sites, or the full AI render report that simulates which passages Google AI Overviews and Bing Copilot may extract, see pricing: Pro is 500,000₫/month (excl. VAT) for 100 scans a day.

Frequently asked questions

Can AI crawlers execute JavaScript?

Most cannot. In 7 days of our log, GPTBot and ClaudeBot fetched 157 and 66 pages but our script only once each; Meta-ExternalAgent and Amazonbot never fetched it. Vercel and MERJ (December 2024) also found that GPTBot, ClaudeBot and PerplexityBot do not execute JavaScript, while Googlebot and AppleBot do.

Does GPTBot download JavaScript files?

Sometimes: once in 7 days here, between pages, like any other URL. Vercel measured 11.50% of ChatGPT crawler requests as JavaScript files, but saw no execution.

Why did Googlebot fetch the script only 5 times for 217 pages?

Most likely because Google's renderer caches resources and may ignore caching headers. 4 of the 5 fetches came 4–6 seconds after a page, the signature of the render step, so 5 of 217 is not a render rate.

How can I check my own log for whether a bot renders my pages?

Pick a script every page loads, count pages and script fetches per user agent, look at the timing, then verify the bot by its published IP ranges or reverse DNS.

Can I trust a user agent that says GPTBot?

No. A user agent is self-declared. OpenAI publishes GPTBot's IP ranges and Google explains reverse-DNS verification for Googlebot; the counts in this post are by declared token.