Which comment systems can an AI crawler actually read? We fetched ten of them the way GPTBot does
Everyone says comments are good for SEO and nobody checks whether a crawler can see them. On 3 September 2026 we fetched pages running WordPress comments, Disqus, Hyvor Talk, giscus, utterances, FastComments, ReplyBox, Cusdis and EchoThread, once as a plain crawler and once in a rendering browser. Without JavaScript, one system showed its comments. With it, all of them did, but seven only inside an iframe, and only one carried structured data. Our own widget is in the table, gap included.
"Comments are good for SEO" is one of the oldest lines in blogging, and "are Disqus comments indexed?" is one of the oldest questions. Both are usually answered from memory. So on 3 September 2026 we measured it: ten comment systems, each on a real public page carrying live comments, fetched twice. Once the way a crawler that does not execute JavaScript fetches a page, with the user agents of GPTBot, ClaudeBot and Googlebot. Once in a headless Chromium that scrolls, waits for the network to go quiet, and then reads every frame and shadow root on the page. We build EchoThread, one of the ten, so treat our sentences as claims and check the table.
The short answer
Without JavaScript, one system out of ten put its comments in the HTML: WordPress's built-in comments. Every hosted or embedded system we tested, Disqus, Hyvor Talk, giscus, utterances, FastComments, ReplyBox, Cusdis and EchoThread's own widget, returned a placeholder element and a script tag. No comment text, no schema.org comment markup. The byte count was identical for all three user agents on every page, so none of these pages serves crawlers a different, pre-rendered version.
With JavaScript, all ten rendered their comments. Six render them inside an iframe served from the vendor's own domain (Disqus, giscus, utterances, FastComments, ReplyBox and Cusdis). Three render them into the page itself (WordPress, Hyvor Talk through a web component, EchoThread). One page carried schema.org structured data describing the comments after rendering, ours, and only on the thread that had comments.
Nobody in the group, including us, shipped the server-side path by default when we measured. EchoThread has an endpoint that returns a thread's discussion as JSON-LD for a publisher to include at render time, and on the morning of the test our own blog, where the widget had gone live hours earlier, did not use it. That row said "no" like everyone else's. We changed that the same afternoon: every article on this blog is now rendered with its approved comments in the HTML the server sends, and describes them on the article's own schema.org markup. The row in the table records the demo thread on our landing page as it was measured, and the last section of this post says what changed.
Who executes JavaScript, according to the crawlers' own documentation
The measurement only matters if some readers of your page do not run JavaScript, so here is what the operators say.
Google renders. Google's documentation states that "Google processes JavaScript web apps in three main phases: 1. Crawling 2. Rendering 3. Indexing", that "Google Search runs JavaScript with an evergreen version of Chromium", and that "once Google's resources allow, a headless Chromium renders the page and executes the JavaScript" (Google Search Central, JavaScript SEO basics). Rendering is a second pass with its own queue, not part of the crawl. On iframes, Google's December 2023 office hours put it this way: "In general, our systems would try to associate the sub-page's content as a part of the primary page for indexing, but it's not guaranteed, since both pages are normal HTML pages on their own too" (Google Search Central office hours, December 2023).
OpenAI and Anthropic do not say. OpenAI's crawler page describes GPTBot as "used to make our generative AI foundation models more useful and safe" and lists its user agent string; it says nothing about executing JavaScript for GPTBot or ChatGPT-User (OpenAI, crawlers). Anthropic's page says ClaudeBot "helps enhance the utility and safety of our generative AI models by collecting web content that could potentially contribute to their training", and is equally silent on rendering (Anthropic, web crawlers). Neither company documents a rendering step. The conservative assumption, and the one this benchmark measures, is that what a plain HTTP fetch returns is what they read.
How we measured
Two conditions, run within the same hour on 3 September 2026.
JS-off. A plain curl -sL of the page with each of three user agents: GPTBot 1.2, ClaudeBot 1.0 and Googlebot 2.1 (the Chrome-flavoured desktop string). We recorded the HTTP status, the byte count, whether the comment system's embed marker was in the HTML, whether any DiscussionForumPosting or "@type":"Comment" JSON-LD was in the HTML, and whether a phrase we had seen inside a rendered comment appeared anywhere in the HTML.
JS-on. Headless Chromium through Playwright with an ordinary desktop Chrome user agent. The script scrolls to the bottom so lazily loaded embeds initialise, waits for network idle plus five seconds, and then inspects the main document and every child frame, walking into shadow roots, for the same two things: JSON-LD comment markup, and leaf elements whose class or id looks like a comment body and carry at least fifteen characters of text. It prints the first two texts it finds so every count in the table was checked by eye.
For each system we used a public page that actually had comments on it, preferring the vendor's own site so nobody can say we picked a badly configured install. Where a first candidate turned out to have an empty thread (Disqus on an independent blog, Hyvor Talk's newest post, FastComments' blog) we kept the result and tested a second page with comments. Commento could not be measured: commento.io returned 404, its demo returned 403, and the tutorials we found for running Commento on your own server either showed only the example snippet or were offline.
| System | Page tested |
|---|---|
| WordPress native comments | ma.tt/2026/07/ai-slop/ (7 comments) |
| EchoThread, thread with comments | echothread.io/ homepage demo thread (13 comments) |
| EchoThread, empty thread | echothread.io/blog/ai-comment-spam-2026/ (widget live, 0 comments) |
| Disqus | blog.disqus.com, the post announcing Disqus Polls |
| Hyvor Talk | hyvor.com/blog/introducing-hyvor-organizations |
| giscus | giscus.app (the project page carries a live thread) |
| utterances | utteranc.es (the project page carries a live thread) |
| FastComments | fastcomments.com/demo |
| ReplyBox | getreplybox.com/demo/ |
| Cusdis | A Hugo blog post on barthpaleologue.github.io with a live thread |
| Commento | No live page found; not measured |
The results
| System | JS-off: comment text in HTML | JS-off: schema.org comment markup | JS-on: comments rendered | Rendered where | JS-on: schema.org comment markup |
|---|---|---|---|---|---|
| WordPress native | Yes, 7 comment bodies | No | Yes | In the page | No |
| EchoThread (13 comments) | No | No | Yes | In the page, no iframe | Yes: 1 DiscussionForumPosting, 8 Comment nodes |
| EchoThread (0 comments) | No, nothing to show | No | "Be the first to comment" | In the page | None emitted for an empty thread |
| Disqus | No | No | Yes | iframe on disqus.com; 13 frames on the page in total, the rest ad and sync frames | No |
| Hyvor Talk | No | No | Yes | In the page, inside a web component | No |
| giscus | No | No | Yes | iframe on giscus.app | No |
| utterances | No | No | Yes | iframe on utteranc.es | No |
| FastComments | No | No | Yes | iframe on fastcomments.com | No |
| ReplyBox | No | No | Yes | iframe on app.getreplybox.com | No |
| Cusdis | No | No | Yes | iframe (srcdoc) in the page | No |
| Commento | Not measured, no live page found | ||||
Two details from the raw log. The JS-off byte counts were identical across GPTBot, ClaudeBot and Googlebot for every page (for example 80,063 bytes for the WordPress post, 47,625 for the Disqus post, 137,573 for the EchoThread homepage), which rules out user-agent-based pre-rendering on any of them. And the phrase check was strict: we took a sentence a browser had just displayed inside a comment, "A couple of years ago, the owner of our website disappeared" on the Hyvor Talk page, "Glory Realm" on ReplyBox, "Test markdown features" on giscus, and grepped the JS-off HTML for it. Zero matches, every time, for every embed.
What this means
A comment in a JavaScript embed does not exist for a crawler that does not render. That is true of every embed we tested, including ours. If the answer engines' crawlers do not execute JavaScript, and their documentation gives no reason to think they do, then years of discussion under your posts are not part of what they read. The one exception in the table is the one that writes comments into the page on the server, which is why WordPress's own comments have always been "indexed" in the way people mean.
For Google, an embed is a two-step bet. Google does render, so a JavaScript-injected comment can be indexed. But rendering happens "once Google's resources allow", and six of the ten systems then place the text in an iframe from another domain, where association with your page is, in Google's words, "not guaranteed". Systems that render into the page itself (Hyvor Talk, EchoThread, WordPress) skip that second step; systems that render into an iframe do not.
Structured data is rarer than rendering. Only one page in the test described its comments in schema.org terms after rendering: EchoThread's widget emits a DiscussionForumPosting document with nested Comment nodes for the visible top-level comments. One honest qualification: Google's guidance for that markup says it is "designed for any forum-style site where people collectively share first-hand perspectives" and lists "an article or blog written directly by an agent for the website (even with comments)" among the things not to use it for (Google Search Central, discussion forum structured data). So on a blog post, that block is valid, machine-readable structured data that any agent parsing JSON-LD can use; it is not a ticket to Google's discussion-forum rich result. Google's own advice for a blog is Article markup with the comments attached to the article. That is what this blog does now: each article's BlogPosting block carries a commentCount, a discussionUrl, and one Comment node per approved comment with replies nested inside it, and the widget's own DiscussionForumPosting block is switched off on these pages so no page describes the same discussion twice. The widget keeps emitting DiscussionForumPosting on pages that are not site-authored articles, which is the case that markup was written for.
The only fix that works for every crawler is to put the comments in the HTML. WordPress does it because the theme renders them. For a hosted system it means the vendor has to give you the thread in a form you can include at build or render time. EchoThread does: GET /api/v1/embed/threads/{thread_id}/discussion.jsonld returns the full nested discussion as JSON-LD, and on the demo thread in the table that is a 7.8 KB document with twelve Comment nodes, readable by anything that can parse JSON. A publisher includes that output in the page server-side and sets data-structured-data="false" on the widget so the page does not carry two blocks. We had not done this on our own blog when we ran the test, and the table says so. We have since done it. Every article here is rendered with its approved comments inside the widget's mount point as a plain nested list, readable without JavaScript, and the article is re-rendered within about half a minute of a comment being posted, approved, edited or removed. Readers with JavaScript see the widget take over the same spot, so nothing changed for them. If you can render server-side, this is the shape to copy: the comments in the HTML, one structured-data block per page, and the interactive layer on top.
Check your own site in three commands
You do not need our script. Pick a sentence from a comment on one of your pages, then:
curl -sL -A 'GPTBot/1.2' https://your-site.example/post/ | grep -c 'a sentence from a comment'
curl -sL -A 'ClaudeBot/1.0' https://your-site.example/post/ | grep -c 'a sentence from a comment'
curl -sL -A 'Googlebot/2.1' https://your-site.example/post/ | grep -c 'a sentence from a comment'A count of 0 means a non-rendering crawler receives a page without that comment. For the rendering case, open the page in Chrome, disable JavaScript in DevTools, and reload: what remains on the screen is roughly what a non-rendering agent gets, and what disappears is what you are trusting the render queue to recover.
If you want the raw log, the user-agent strings and the two scripts, they are dated 3 September 2026 and the method above is complete enough to reproduce the table on any set of pages. Numbers move as comments are posted, so if you re-run it, record the date.
EchoThread's free plan is one site with comments included, no ads and no reader tracking; the pricing page has the rest, and the add comments to any website guide covers the install.
Discussion
Comments
This thread runs on EchoThread — the same widget you would add to your own site.
No comments yet.