[{"data":1,"prerenderedAt":874},["ShallowReactive",2],{"blog-generative-engine-optimization-audit-en":3},{"slug":4,"published":5,"minutes":6,"category":7,"tags":8,"keywords":13,"about":23,"sources":30,"cover":55,"og":56,"expertise":57,"locales":58,"lang":59,"title":62,"description":63,"coverAlt":64,"metaTitle":65,"takeaways":66,"faq":72,"toc":88,"blocks":116,"others":670},"generative-engine-optimization-audit","2026-09-28",12,"web",[9,10,11,12],"GEO","AI search","Structured data","nginx",[14,15,16,17,18,19,20,21,22],"generative engine optimization","GEO audit","GEO checklist","AI search optimization","AI Overviews optimization","ChatGPT search citations","llms.txt describedby","AI crawler logs nginx","Bing AI Performance report",[24,27],{"name":25,"url":26},"Generative engine optimization","https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FGenerative_engine_optimization",{"name":28,"url":29},"Search engine optimization","https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FSearch_engine_optimization",[31,34,37,40,43,46,49,52],{"title":32,"url":33},"Aggarwal et al.: GEO: Generative Engine Optimization (KDD 2024)","https:\u002F\u002Farxiv.org\u002Fabs\u002F2311.09735",{"title":35,"url":36},"Martinez: Optimizing Visibility in Generative Engines, a critical survey of GEO 2023–2026 (July 2026)","https:\u002F\u002Farxiv.org\u002Fabs\u002F2607.14035",{"title":38,"url":39},"Tian et al.: Diagnosing and Repairing Citation Failures in Generative Engine Optimization (March 2026)","https:\u002F\u002Farxiv.org\u002Fabs\u002F2603.09296",{"title":41,"url":42},"Google Search Central: AI features and your website","https:\u002F\u002Fdevelopers.google.com\u002Fsearch\u002Fdocs\u002Fappearance\u002Fai-features",{"title":44,"url":45},"Google Search Central: Build and submit a sitemap","https:\u002F\u002Fdevelopers.google.com\u002Fsearch\u002Fdocs\u002Fcrawling-indexing\u002Fsitemaps\u002Fbuild-sitemap",{"title":47,"url":48},"OpenAI: Overview of OpenAI crawlers","https:\u002F\u002Fdevelopers.openai.com\u002Fapi\u002Fdocs\u002Fbots",{"title":50,"url":51},"Bing Webmaster Blog: Introducing AI Performance in Bing Webmaster Tools (10 February 2026)","https:\u002F\u002Fblogs.bing.com\u002Fwebmaster\u002FFebruary-2026\u002FIntroducing-AI-Performance-in-Bing-Webmaster-Tools-Public-Preview",{"title":53,"url":54},"llmstxt.org: Changes from v1 to v2","https:\u002F\u002Fllmstxt.org\u002Fchanges.html","\u002Fimages\u002Fblog\u002Fgenerative-engine-optimization-audit\u002Fcover.webp","\u002Fimages\u002Fblog\u002Fgenerative-engine-optimization-audit\u002Fog.jpg","ai-engineer",[59,60,61],"en","de","hu","Generative engine optimization in practice: a full GEO audit of my own site","What generative engine optimization is, what the research really supports, and the GEO audit I ran on this site: 12 checks, 7 fixes, code included.","A five-step pipeline from crawl to index, retrieve, cite and measure, showing where a page can drop out of an AI-generated answer.","Generative engine optimization audit · Balázs Csorba",[67,68,69,70,71],"Generative engine optimization (GEO) means making pages easy for AI answer engines to retrieve, quote and cite; the term comes from a 2023 paper by Aggarwal et al., presented at KDD 2024.","In that paper, adding quotations, statistics and cited sources raised a page's visibility in generated answers by up to 40%, while keyword stuffing did worse than no optimization at all.","A July 2026 survey of 45 GEO studies found those gains hold only for pages that are already retrieved; no technique showed a stable effect on being found in the first place.","Google says AI Overviews and AI Mode need no special files or schema: a page must be indexed and eligible for a snippet, so the SEO basics are the entry ticket.","My audit of this site's 105 pages ran 12 checks and led to 7 fixes, mostly discovery links, honest dates and a way to see AI crawler traffic; the code for each is below.",[73,76,79,82,85],{"q":74,"a":75},"What is generative engine optimization (GEO)?","Generative engine optimization is the practice of making content easy for AI answer engines such as Google AI Overviews and AI Mode, ChatGPT search, Perplexity and Copilot to retrieve, quote and cite. The term comes from a paper by Aggarwal et al., first published in November 2023 and presented at KDD 2024, which showed that adding quotations, statistics and cited sources can raise a page's visibility in generated answers by up to 40%.",{"q":77,"a":78},"Is GEO different from SEO?","It builds on SEO rather than replacing it. SEO aims for a high position in a list of links; GEO aims to be one of the few sources an answer is built from, so the unit is the quotable passage rather than the page. For Google's AI features the entry requirements are the same as for search: the page must be indexed and eligible for a snippet.",{"q":80,"a":81},"Does llms.txt help with generative engine optimization?","Not as a ranking signal. Google says no AI text files or special markup are needed to appear in AI Overviews or AI Mode, and log studies show most llms.txt files are never requested. llms.txt and Markdown copies help agents that fetch pages directly, which is a separate audience; on a static site they are cheap to keep.",{"q":83,"a":84},"What content changes improve AI citations?","The best-supported ones are specific, verifiable claims: statistics, quotations and links to credible sources, in text that clearly matches the question being asked. Keyword stuffing performed worse than no optimization in the original study. A 2026 survey of 45 studies warns that these gains apply to pages that are already retrieved and that generic rewrite rules transfer poorly, so measure on your own pages.",{"q":86,"a":87},"How do I measure GEO results?","Combine three sources. Bing Webmaster Tools' AI Performance report shows citations in Copilot and Bing's AI answers per page, with the grounding queries behind them. Server logs show which pages AI crawlers fetch. Referral traffic from chatgpt.com, perplexity.ai and similar sites shows up in analytics. Generated answers vary between runs, so repeat the same prompts over time instead of trusting a single answer.",[89,92,95,98,101,104,107,110,113],{"id":90,"title":91},"what-is-geo","What is generative engine optimization?",{"id":93,"title":94},"what-the-research-shows","What does the research actually show?",{"id":96,"title":97},"what-platforms-say","What do Google, OpenAI and Microsoft say?",{"id":99,"title":100},"audit-method","How I ran the audit",{"id":102,"title":103},"audit-findings","What did the audit find?",{"id":105,"title":106},"fixes","The fixes, step by step",{"id":108,"title":109},"what-i-did-not-do","What I deliberately did not do",{"id":111,"title":112},"checklist","GEO audit checklist",{"id":114,"title":115},"sources","Sources",[117,130,133,136,143,152,189,192,193,200,224,231,238,245,246,249,294,303,304,315,327,329,332,333,439,442,443,447,461,463,485,487,490,496,514,517,527,529,532,535,537,540,542,545,546,573,574,627,643,644],{"type":118,"content":119},"paragraph",[120,124,125,129],{"tag":121,"children":122},"strong",[123],"Generative engine optimization (GEO)"," is the practice of making content easy for AI answer engines, such as Google AI Overviews and AI Mode, ChatGPT search, Perplexity and Copilot, to find, retrieve, quote and cite. Where SEO optimizes for a position in a list of links, GEO optimizes for being one of the few sources an answer is built from. The term comes from a ",{"tag":126,"href":33,"children":127},"a",[128],"2023 research paper"," presented at KDD 2024.",{"type":118,"content":131},[132],"This post has two halves: what the evidence supports, which is less than most GEO guides claim, and a full GEO audit of this site, a static Nuxt build with 105 pages in three languages. The audit ran 12 checks and led to 7 fixes; the code for each is below, and the checklist at the end is the one I would run on any site.",{"type":134,"level":135,"id":90,"text":91},"heading",2,{"type":118,"content":137},[138,139,142],"A generative engine does not rank ten links. It decides whether a question needs a web search at all, sends one or more queries (Google calls this ",{"tag":121,"children":140},[141],"query fan-out","), pulls candidate pages from an index, picks passages to put into the model's context, writes the answer and cites some of its sources. A page can drop out at every one of those steps, and each step has different levers.",{"type":144,"attrs":145,"inner":149,"caption":150},"diagram",{"viewBox":146,"role":147,"aria-labelledby":148},"0 0 720 320","img","geo1-t geo1-d","\u003Ctitle id=\"geo1-t\">Where a page can drop out of an AI answer\u003C\u002Ftitle>\u003Cdesc id=\"geo1-d\">Five steps from left to right: crawl, index, retrieve, cite, measure. Under each step is what a site owner controls: robots.txt and AI user agents for crawling; being indexable and snippet-eligible for indexing; on-topic text and a Markdown copy for retrieval; facts, sources and clear claims for citation; AI citation reports and agent logs for measurement. The typical failure at each step: blocked, no snippet, not relevant, vague text, invisible. Classic SEO covers crawl to retrieve, GEO extends from retrieve to measure.\u003C\u002Fdesc>\u003Ctext x=\"20\" y=\"26\" class=\"d-title\">From crawl to citation\u003C\u002Ftext>\u003Crect x=\"20\" y=\"44\" width=\"120\" height=\"60\" rx=\"8\" class=\"d-sky\" \u002F>\u003Ctext x=\"80\" y=\"79\" text-anchor=\"middle\" class=\"d-text\">Crawl\u003C\u002Ftext>\u003Cpath d=\"M142 74H154\" class=\"d-line\" \u002F>\u003Cpath d=\"M154 70L159 74L154 78Z\" class=\"d-head\" \u002F>\u003Ctext x=\"80\" y=\"160\" text-anchor=\"middle\" class=\"d-small\">robots.txt\u003C\u002Ftext>\u003Ctext x=\"80\" y=\"180\" text-anchor=\"middle\" class=\"d-small\">AI user agents\u003C\u002Ftext>\u003Ctext x=\"80\" y=\"262\" text-anchor=\"middle\" class=\"d-small\">blocked\u003C\u002Ftext>\u003Crect x=\"160\" y=\"44\" width=\"120\" height=\"60\" rx=\"8\" class=\"d-sky\" \u002F>\u003Ctext x=\"220\" y=\"79\" text-anchor=\"middle\" class=\"d-text\">Index\u003C\u002Ftext>\u003Cpath d=\"M282 74H294\" class=\"d-line\" \u002F>\u003Cpath d=\"M294 70L299 74L294 78Z\" class=\"d-head\" \u002F>\u003Ctext x=\"220\" y=\"160\" text-anchor=\"middle\" class=\"d-small\">indexable\u003C\u002Ftext>\u003Ctext x=\"220\" y=\"180\" text-anchor=\"middle\" class=\"d-small\">max-snippet\u003C\u002Ftext>\u003Ctext x=\"220\" y=\"262\" text-anchor=\"middle\" class=\"d-small\">no snippet\u003C\u002Ftext>\u003Crect x=\"300\" y=\"44\" width=\"120\" height=\"60\" rx=\"8\" class=\"d-sky\" \u002F>\u003Ctext x=\"360\" y=\"79\" text-anchor=\"middle\" class=\"d-text\">Retrieve\u003C\u002Ftext>\u003Cpath d=\"M422 74H434\" class=\"d-line\" \u002F>\u003Cpath d=\"M434 70L439 74L434 78Z\" class=\"d-head\" \u002F>\u003Ctext x=\"360\" y=\"160\" text-anchor=\"middle\" class=\"d-small\">on-topic text\u003C\u002Ftext>\u003Ctext x=\"360\" y=\"180\" text-anchor=\"middle\" class=\"d-small\">Markdown copy\u003C\u002Ftext>\u003Ctext x=\"360\" y=\"262\" text-anchor=\"middle\" class=\"d-small\">not relevant\u003C\u002Ftext>\u003Crect x=\"440\" y=\"44\" width=\"120\" height=\"60\" rx=\"8\" class=\"d-accent\" \u002F>\u003Ctext x=\"500\" y=\"79\" text-anchor=\"middle\" class=\"d-text\">Cite\u003C\u002Ftext>\u003Cpath d=\"M562 74H574\" class=\"d-line\" \u002F>\u003Cpath d=\"M574 70L579 74L574 78Z\" class=\"d-head\" \u002F>\u003Ctext x=\"500\" y=\"160\" text-anchor=\"middle\" class=\"d-small\">facts, sources\u003C\u002Ftext>\u003Ctext x=\"500\" y=\"180\" text-anchor=\"middle\" class=\"d-small\">clear claims\u003C\u002Ftext>\u003Ctext x=\"500\" y=\"262\" text-anchor=\"middle\" class=\"d-small\">vague text\u003C\u002Ftext>\u003Crect x=\"580\" y=\"44\" width=\"120\" height=\"60\" rx=\"8\" class=\"d-mint\" \u002F>\u003Ctext x=\"640\" y=\"79\" text-anchor=\"middle\" class=\"d-text\">Measure\u003C\u002Ftext>\u003Ctext x=\"640\" y=\"160\" text-anchor=\"middle\" class=\"d-small\">AI citations\u003C\u002Ftext>\u003Ctext x=\"640\" y=\"180\" text-anchor=\"middle\" class=\"d-small\">agent logs\u003C\u002Ftext>\u003Ctext x=\"640\" y=\"262\" text-anchor=\"middle\" class=\"d-small\">invisible\u003C\u002Ftext>\u003Ctext x=\"20\" y=\"134\" class=\"d-label\">WHAT YOU CONTROL\u003C\u002Ftext>\u003Cpath d=\"M20 206H700\" class=\"d-line d-dash\" \u002F>\u003Ctext x=\"20\" y=\"236\" class=\"d-label\">TYPICAL FAILURE\u003C\u002Ftext>\u003Cpath d=\"M20 284H420\" class=\"d-line-accent\" \u002F>\u003Ctext x=\"220\" y=\"306\" text-anchor=\"middle\" class=\"d-label\">SEO\u003C\u002Ftext>\u003Cpath d=\"M300 292H700\" class=\"d-line-accent d-dash\" \u002F>\u003Ctext x=\"560\" y=\"306\" text-anchor=\"middle\" class=\"d-label\">GEO\u003C\u002Ftext>",[151],"Five places a page can drop out of an AI-generated answer. Under each step: what a site owner controls, and the typical failure. Classic SEO covers crawling, indexing and retrieval; GEO adds what happens once a page is retrieved: whether it is cited, and whether you can see that it was.",{"type":153,"head":154,"rows":160},"table",[155,157,159],[156],"",[158],"SEO",[9],[161,168,175,182],[162,164,166],[163],"Goal",[165],"A high position in a list of links",[167],"Being one of the few sources an answer is built from",[169,171,173],[170],"Unit",[172],"The page",[174],"The passage or fact that gets quoted",[176,178,180],[177],"Success metric",[179],"Position, clicks",[181],"Citations, mentions, referral visits",[183,185,187],[184],"Measured with",[186],"Search Console, analytics",[188],"Bing AI Performance, server logs, repeated prompts",{"type":118,"content":190},[191],"GEO does not replace SEO. For Google's AI features it sits on top of it: a page that is not indexed cannot be cited.",{"type":134,"level":135,"id":93,"text":94},{"type":118,"content":194},[195,196,199],"The founding paper is ",{"tag":126,"href":33,"children":197},[198],"GEO: Generative Engine Optimization"," by Pranjal Aggarwal and colleagues from Princeton and other institutions, first published in November 2023 and accepted at KDD 2024. They built GEO-bench, a benchmark of 10,000 queries, rewrote source pages with nine different methods and measured how visible each source became in the generated answer.",{"type":201,"ordered":202,"items":203},"list",false,[204,209,214,219],[205,208],{"tag":121,"children":206},[207],"Quotations, statistics and cited sources work."," The best methods improved on the unoptimized baseline by 41% on position-adjusted word count and by 28% on a subjective impression score; the headline figure is \"up to 40%\".",[210,213],{"tag":121,"children":211},[212],"Keyword stuffing does not."," Adding more query keywords, the classic SEO move, scored below the baseline.",[215,218],{"tag":121,"children":216},[217],"Lower-ranked pages gain most."," Citing sources raised the visibility of pages ranked fifth in the search results by 115.1%, while top-ranked pages lost 30.3% on average.",[220,223],{"tag":121,"children":221},[222],"It carried over to a live engine."," On Perplexity.ai, the same methods raised visibility by up to 37%.",{"type":118,"content":225},[226,227,230],"Then the caveats arrived. Olivier Martinez's ",{"tag":126,"href":36,"children":228},[229],"critical survey of 45 GEO studies"," (July 2026) argues that GEO is not a single ranking task but a noisy pipeline, and that the founding paper's gains are valid in its setting but conditional on a source already sitting in a fixed context. In the reviewed work, topical relevance and position in the context were the most reproducible levers. Generic rewriting heuristics transferred poorly, citation-oriented rewrites could even hurt retrieval, and no technique showed a stable, long-term, cross-platform effect on being discovered in the first place.",{"type":118,"content":232},[233,234,237],"A ",{"tag":126,"href":39,"children":235},[236],"March 2026 paper by Tian and colleagues"," points the same way from the other side. Instead of applying one rewrite to every page, their AgentGEO system diagnoses why a specific document is not cited and repairs that. It raised citation rates by over 40% relative while changing about 5% of the content, and the authors found that generic optimization can harm long-tail content.",{"type":239,"variant":240,"title":241,"body":242},"callout","note","My reading of the evidence",[243],[244],"Two things are well supported. First, a page has to be retrievable: crawlable, indexed, eligible for a snippet and clearly on topic. Second, once it is retrieved, specific, verifiable and sourced text gets used more than vague text. Everything beyond that is a hypothesis until you measure it on your own pages.",{"type":134,"level":135,"id":96,"text":97},{"type":118,"content":247},[248],"The platforms' own documentation is short and consistent.",{"type":201,"ordered":202,"items":250},[251,276,285],[252,255,256,259,260,264,265,264,268,271,272,275],{"tag":121,"children":253},[254],"Google"," says there are ",{"tag":126,"href":42,"children":257},[258],"no additional requirements"," for AI Overviews or AI Mode: a page must be indexed and eligible to be shown with a snippet. You don't need new machine-readable files, AI text files or special schema.org markup; structured data must match the visible text; and ",{"tag":261,"children":262},"code",[263],"nosnippet",", ",{"tag":261,"children":266},[267],"data-nosnippet",{"tag":261,"children":269},[270],"max-snippet"," and ",{"tag":261,"children":273},[274],"noindex"," control what is shown. Both features may use query fan-out, and their traffic is counted in Search Console under the Web search type.",[277,280,281,284],{"tag":121,"children":278},[279],"OpenAI"," uses ",{"tag":126,"href":48,"children":282},[283],"OAI-SearchBot"," for ChatGPT search and GPTBot for training, and the two settings are independent. Sites that block OAI-SearchBot are not shown in ChatGPT search answers, apart from navigational links, and a robots.txt change takes about 24 hours to apply.",[286,289,290,293],{"tag":121,"children":287},[288],"Microsoft"," added ",{"tag":126,"href":51,"children":291},[292],"AI Performance"," to Bing Webmaster Tools as a public preview on 10 February 2026. It reports total citations in Copilot and Bing's AI answers, the average number of cited pages, the grounding queries the AI used to retrieve content, and citations per URL.",{"type":118,"content":295},[296,297,302],"Note what Google leaves out: llms.txt and Markdown copies are not needed to appear in its AI features. They serve agents that fetch pages directly, which is a different audience; my post on ",{"tag":298,"to":299,"children":300},"link","\u002Fblog\u002Fllms-txt-vs-markdown-content-negotiation",[301],"llms.txt versus Markdown content negotiation"," has the log data. They cost little on a static site, so I keep them, but I don't count them as ranking levers.",{"type":134,"level":135,"id":99,"text":100},{"type":118,"content":305},[306,307,310,311,314],"I ran the audit on the production build, not the source code: ",{"tag":261,"children":308},[309],"nuxt generate"," writes 105 static HTML pages (35 pages in English, German and Hungarian), and a post-build step writes a Markdown copy of each one plus ",{"tag":261,"children":312},[313],"\u002Fllms.txt",". Two small Node scripts then went through the output.",{"type":201,"ordered":202,"items":316},[317,322],[318,321],{"tag":121,"children":319},[320],"Technical audit"," over every generated HTML file: title and description, canonical and hreflang, robots directives, Markdown and llms.txt links, the JSON-LD graph (node types and dates), one h1 per page, and whether the Markdown copy exists.",[323,326],{"tag":121,"children":324},[325],"Content audit"," over the 24 English posts in the blog database: does the first sentence define the topic, how many sources and inline citations, how many numbers, whether there are key takeaways and an FAQ, and how many h2 headings are questions.",{"type":261,"code":328},"\u002F\u002F GEO audit over the generated site (excerpt): one pass over every HTML file\nfor (const file of htmlFiles) {\n  const html = readFileSync(file, 'utf8')\n  if (!\u002Ftype=\"text\\\u002Fmarkdown\"\u002F.test(html)) add('no rel=alternate text\u002Fmarkdown', page)\n  if (!\u002Frel=\"describedby\"\u002F.test(html)) add('no rel=describedby llms.txt', page)\n  const robots = html.match(\u002F\u003Cmeta name=\"robots\" content=\"([^\"]*)\"\u002F)?.[1] ?? ''\n  if (!robots.includes('max-snippet')) add('robots without max-snippet', page)\n  for (const node of jsonLdGraph(html))\n    if (\u002FWebPage|CollectionPage|ProfilePage\u002F.test(node['@type']) && !node.dateModified) add('page node without a date', page)\n}",{"type":118,"content":330},[331],"The checks follow the pipeline in the diagram: access, discovery, understanding, content and measurement. The findings below are in the same order.",{"type":134,"level":135,"id":102,"text":103},{"type":153,"head":334,"rows":341},[335,337,339],[336],"Check",[338],"Before",[340],"After",[342,349,359,365,376,389,395,402,409,419,426,432],[343,345,347],[344],"AI crawlers allowed",[346],"Pass: robots.txt allows everyone and names 16 AI user agents; TDMRep allows text and data mining",[348],"Unchanged",[350,352,354],[351],"Snippet eligibility",[353],"Indexable, but 33 of 105 pages set no max-snippet directive",[355,358],{"tag":261,"children":356},[357],"max-snippet:-1"," on every indexable page",[360,362,364],[361],"Markdown copy per page",[363],"Pass: 105 of 105",[348],[366,372,374],[367,368,371],"Markdown discovery (",{"tag":261,"children":369},[370],"rel=\"alternate\"",")",[373],"21 of 105 pages; no blog post or expertise page had it",[375],"Every page, plus an HTTP Link header",[377,382,387],[378,379,371],"llms.txt discovery (",{"tag":261,"children":380},[381],"rel=\"describedby\"",[383,384],"0 of 105; llms.txt was linked as ",{"tag":261,"children":385},[386],"rel=\"alternate\" type=\"text\u002Fplain\"",[388],"Every page, plus the Link header",[390,392,394],[391],"Entity graph (JSON-LD)",[393],"Pass: Person, WebSite, WebPage and BreadcrumbList on every page; BlogPosting, FAQPage, citations and speakable on posts",[348],[396,398,400],[397],"Dates on page nodes",[399],"0 of 105 WebPage nodes had a date",[401],"Posts and the blog index carry datePublished and dateModified",[403,405,407],[404],"Sitemap lastmod",[406],"Every static page stamped with the build date",[408],"lastmod only where there is a real date",[410,412,414],[411],"Quotable summary",[413],"Key takeaways marked as speakable, but not in the schema",[415,418],{"tag":261,"children":416},[417],"abstract"," built from the takeaways",[420,422,424],[421],"Answer-first intros",[423],"21 of 24 posts open with a definition; 3 are first-person build logs",[425],"Kept as they are",[427,428,430],[115],[429],"Median of 6 sources per post; 3 posts cite fewer than 4",[431],"Noted for their next revision",[433,435,437],[434],"AI crawler visibility",[436],"None: analytics loads only after consent, and crawlers don't run JavaScript",[438],"A separate nginx log for AI agents, with the Accept header",{"type":118,"content":440},[441],"The audit also flagged about 30 page titles and descriptions that are longer than search results display. That is snippet hygiene rather than GEO, and I left it for a separate pass. The content side held up because the posts were written to a template with takeaways, an FAQ and a source list from the start; the gaps were almost all in the plumbing.",{"type":134,"level":135,"id":105,"text":106},{"type":134,"level":444,"id":445,"text":446},3,"markdown-discovery","Every page links its Markdown copy and llms.txt",{"type":118,"content":448},[449,450,453,454,457,458,460],"Version 2 of llms.txt answers the question of how an agent finds a page's Markdown version with ",{"tag":126,"href":54,"children":451},[452],"two standard link relations",": ",{"tag":261,"children":455},[456],"rel=\"alternate\" type=\"text\u002Fmarkdown\""," for the Markdown copy, and ",{"tag":261,"children":459},[381]," for the llms.txt that covers the page, either as HTML link elements or as an HTTP Link header. The site had a plugin for the first one, limited to seven top-level pages. It now covers every page that has a copy, error pages excluded:",{"type":261,"code":462},"\u002F\u002F app\u002Fplugins\u002Fagent-links.ts (excerpt)\nconst PAGE = \u002F^(\\\u002F(about|references|game|accessibility|privacy|imprint|blog)|\\\u002F(blog|expertise)\\\u002F[a-z0-9-]+)?$\u002F\n\nif (!m || error.value || !PAGE.test(page)) return {}\nreturn {\n  link: [\n    { key: 'markdown', rel: 'alternate', type: 'text\u002Fmarkdown', href: page ? `${prefix}${page}.md` : `${prefix}\u002Findex.md` },\n    { key: 'llms-txt', rel: 'describedby', href: '\u002Fllms.txt', title: 'llms.txt' },\n  ],\n}",{"type":118,"content":464},[465,466,469,470,473,474,477,478,480,481,484],"The same pair goes out as an HTTP header, so a client that only sends a HEAD request sees it too. One nginx detail needed a second look: ",{"tag":261,"children":467},[468],"try_files"," changes ",{"tag":261,"children":471},[472],"$uri"," to ",{"tag":261,"children":475},[476],"\u002Fabout\u002Findex.html"," before the headers are written, so a map on ",{"tag":261,"children":479},[472]," produces nothing for most pages. Keying the map on ",{"tag":261,"children":482},[483],"$request_uri"," avoids that:",{"type":261,"code":486},"# Keyed on $request_uri: try_files changes $uri to \u002Fpage\u002Findex.html before the headers go out\nmap $request_uri $bc_agent_links {\n  default                                  \"\";\n  \"~^\u002F(\\?.*)?$\"                            '\u003C\u002Findex.md>; rel=\"alternate\"; type=\"text\u002Fmarkdown\", \u003C\u002Fllms.txt>; rel=\"describedby\"';\n  \"~^(?\u003Cp>\u002F[a-z0-9\u002F-]*[a-z0-9])(\\?.*)?$\"   '\u003C$p.md>; rel=\"alternate\"; type=\"text\u002Fmarkdown\", \u003C\u002Fllms.txt>; rel=\"describedby\"';\n}\n\nlocation \u002F {\n  add_header Link $bc_agent_links;   # an empty value sends no header\n  # ...\n}",{"type":134,"level":444,"id":488,"text":489},"snippets-and-dates","Snippet directives and honest dates",{"type":118,"content":491},[492,493,495],"Google only uses a page in AI Overviews if it may show a snippet. Snippets are allowed by default, so ",{"tag":261,"children":494},[357]," changes nothing in principle, but it states the intent on every page and matches what the blog posts already sent.",{"type":118,"content":497},[498,499,502,503,506,507,271,510,513],"Dates were the more interesting gap. The sitemap gave every static page the date of the build, which is a false freshness signal: Google says it uses ",{"tag":261,"children":500},[501],"lastmod"," only when the value is ",{"tag":126,"href":45,"children":504},[505],"consistently and verifiably accurate",". Static pages now have no lastmod at all, and only the posts and the blog index, which have real dates, carry ",{"tag":261,"children":508},[509],"datePublished",{"tag":261,"children":511},[512],"dateModified"," in their structured data. No date is better than a wrong one.",{"type":134,"level":444,"id":515,"text":516},"quotable-summary","A quotable summary in the structured data",{"type":118,"content":518},[519,520,523,524,526],"Every post starts with five key takeaways, written as sentences that stand on their own. They were already marked as speakable, and the post's sources were already in the schema as ",{"tag":261,"children":521},[522],"citation",". The takeaways now also go into the BlogPosting's ",{"tag":261,"children":525},[417],", so a system that reads the JSON-LD gets the summary without parsing the page:",{"type":261,"code":528},"{\n  \"@type\": \"BlogPosting\",\n  \"headline\": \"Generative engine optimization in practice: …\",\n  \"abstract\": \"Generative engine optimization (GEO) means … (the five key takeaways)\",\n  \"datePublished\": \"2026-09-28\",\n  \"dateModified\": \"2026-09-28\",\n  \"citation\": [{ \"@type\": \"CreativeWork\", \"name\": \"GEO: Generative Engine Optimization\", \"url\": \"https:\u002F\u002Farxiv.org\u002Fabs\u002F2311.09735\" }],\n  \"speakable\": { \"@type\": \"SpeakableSpecification\", \"cssSelector\": [\"#takeaways\"] }\n}",{"type":134,"level":444,"id":530,"text":531},"measuring-ai-crawlers","Measuring what AI crawlers fetch",{"type":118,"content":533},[534],"You can't improve what you can't see, and this site could not see AI crawlers at all: Google Analytics only loads after cookie consent, and crawlers don't run JavaScript anyway. The server log is the only honest source. nginx now writes requests from 16 AI user-agent patterns to a log of their own, with the Accept header, so it shows both which pages they fetch and who asks for Markdown:",{"type":261,"code":536},"# http context: which requests come from AI crawlers and agents\nmap $http_user_agent $bc_ai_agent {\n  default 0;\n  \"~*(GPTBot|OAI-SearchBot|ChatGPT-User|ClaudeBot|Claude-SearchBot|Claude-User|PerplexityBot|Perplexity-User|…)\" 1;\n}\nlog_format bc_agents '$time_iso8601 $status $request_method $host$request_uri -> $uri $body_bytes_sent \"$http_accept\" \"$http_user_agent\"';\n\n# server block: an access_log here replaces the inherited one, so the default is repeated\naccess_log \u002Fvar\u002Flog\u002Fnginx\u002Faccess.log;\naccess_log \u002Fvar\u002Flog\u002Fnginx\u002Fbalazscsorba-agents.log bc_agents if=$bc_ai_agent;",{"type":118,"content":538},[539],"Two commands are enough to start with:",{"type":261,"code":541},"# The pages AI agents fetch most (after negotiation, so \u002Fabout.md means \"asked for Markdown\")\nawk '{print $6}' \u002Fvar\u002Flog\u002Fnginx\u002Fbalazscsorba-agents.log | sort | uniq -c | sort -rn | head -20\n# Requests per agent\ngrep -oE '(GPTBot|OAI-SearchBot|ChatGPT-User|ClaudeBot|Claude-SearchBot|Claude-User|PerplexityBot|Perplexity-User)' \\\n  \u002Fvar\u002Flog\u002Fnginx\u002Fbalazscsorba-agents.log | sort | uniq -c | sort -rn",{"type":118,"content":543},[544],"Server logs show crawling, not citing. For citations, the AI Performance report in Bing Webmaster Tools is the one first-party number available; Search Console includes AI Overviews and AI Mode in its Web totals; and referrals from chatgpt.com, perplexity.ai and copilot.microsoft.com show up in analytics like any other referrer. Beyond that, the survey's advice applies: repeat the same prompts over time, with paraphrases, because single answers vary from run to run.",{"type":134,"level":135,"id":108,"text":109},{"type":201,"ordered":202,"items":547},[548,553,558,563,568],[549,552],{"tag":121,"children":550},[551],"No keyword stuffing."," It scored below doing nothing in the original GEO paper.",[554,557],{"tag":121,"children":555},[556],"No blanket rewrite of all 24 posts."," Generic rewrite rules transfer poorly and can hurt retrieval, according to the survey. The three posts with few sources get more when they are next revised, for the readers' sake.",[559,562],{"tag":121,"children":560},[561],"No hidden text for language models."," Text meant only for AI systems is cloaking by another name and uses the same trick as prompt injection. Everything a model can read on this site, a person can read too.",[564,567],{"tag":121,"children":565},[566],"No structured data that isn't on the page."," Google asks for markup that matches the visible text; the FAQ in the schema is the FAQ you see.",[569,572],{"tag":121,"children":570},[571],"No invented entity data."," The Person node lists only profiles that exist. More sameAs links are on my list, but only for accounts I actually use.",{"type":134,"level":135,"id":111,"text":112},{"type":201,"ordered":575,"items":576},true,[577,582,587,592,597,602,607,612,617,622],[578,581],{"tag":121,"children":579},[580],"Let the answer engines in."," robots.txt allows OAI-SearchBot, Claude-SearchBot, PerplexityBot and the rest, and no CDN bot filter overrides it.",[583,586],{"tag":121,"children":584},[585],"Be indexed and snippet-eligible."," No stray noindex or nosnippet; check in Search Console and Bing Webmaster Tools.",[588,591],{"tag":121,"children":589},[590],"Serve the content as HTML."," Crawlers that don't run JavaScript must see the text; static or server rendering does that.",[593,596],{"tag":121,"children":594},[595],"Open with the answer."," The first sentence of a page defines its topic in the words someone would search for.",[598,601],{"tag":121,"children":599},[600],"Make claims specific and sourced."," Numbers, dates, named sources and links are the part of GEO the research supports best.",[603,606],{"tag":121,"children":604},[605],"Keep structured data true."," One connected entity graph, markup that matches the visible text, and dates only where they are real.",[608,611],{"tag":121,"children":609},[610],"Offer a clean copy."," A Markdown version of each page, linked with rel=\"alternate\", and an llms.txt, linked with rel=\"describedby\".",[613,616],{"tag":121,"children":614},[615],"Log AI crawlers separately."," A server log with the user agent and the Accept header, not client-side analytics.",[618,621],{"tag":121,"children":619},[620],"Track citations, not just rankings."," Bing AI Performance, Search Console and referral traffic, checked over weeks.",[623,626],{"tag":121,"children":624},[625],"Re-run the audit after every build."," The checks are scripts, so a regression shows up the same day.",{"type":118,"content":628},[629,630,632,633,637,638,642],"For the agent side of the same work, see ",{"tag":298,"to":299,"children":631},[301]," and the guide to ",{"tag":298,"to":634,"children":635},"\u002Fblog\u002Fwebmcp-agent-ready-website-guide",[636],"WebMCP on a real site",". If you want this audit run on your own site, ",{"tag":298,"to":639,"children":640},"\u002Fabout",[641],"get in touch",".",{"type":134,"level":135,"id":114,"text":115},{"type":201,"ordered":575,"items":645},[646,649,652,655,658,661,664,667],[647],{"tag":126,"href":33,"children":648},[32],[650],{"tag":126,"href":36,"children":651},[35],[653],{"tag":126,"href":39,"children":654},[38],[656],{"tag":126,"href":42,"children":657},[41],[659],{"tag":126,"href":45,"children":660},[44],[662],{"tag":126,"href":48,"children":663},[47],[665],{"tag":126,"href":51,"children":666},[50],[668],{"tag":126,"href":54,"children":669},[53],[671,719,774,819],{"slug":672,"published":673,"minutes":674,"category":7,"tags":675,"keywords":681,"about":690,"sources":699,"cover":712,"og":713,"expertise":714,"locales":715,"lang":59,"title":716,"description":717,"coverAlt":718},"home-assistant-ev-charging-energy-manager","2026-09-29",9,[676,677,678,679,680],"Home Assistant","EPEX Spot","Energy prices","Tesla","Energy management",[682,683,684,685,686,687,688,689],"EPEX Austria prices","spot tariff Austria EV charging","Home Assistant EV charging","cheapest hours charging","day-ahead price Austria","negative electricity prices Austria","Tesla charging spot prices","load management 3 phase",[691,694,696],{"name":692,"url":693},"EPEX SPOT","https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FEPEX_SPOT",{"name":676,"url":695},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FHome_Assistant",{"name":697,"url":698},"Load management","https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FLoad_management",[700,703,706,709],{"title":701,"url":702},"aWATTar Austria: market data API","https:\u002F\u002Fwww.awattar.at\u002Fservices\u002Fapi",{"title":704,"url":705},"EPEX SPOT: market results","https:\u002F\u002Fwww.epexspot.com\u002Fen\u002Fmarket-results",{"title":707,"url":708},"Home Assistant developer docs: apps","https:\u002F\u002Fdevelopers.home-assistant.io\u002Fdocs\u002Fapps\u002F",{"title":710,"url":711},"Home Assistant: Tesla Fleet integration","https:\u002F\u002Fwww.home-assistant.io\u002Fintegrations\u002Ftesla_fleet\u002F","\u002Fimages\u002Fblog\u002Fhome-assistant-ev-charging-energy-manager\u002Fcover.webp","\u002Fimages\u002Fblog\u002Fhome-assistant-ev-charging-energy-manager\u002Fog.jpg","vue-nuxt-developer",[59,60,61],"Charging on EPEX Austria prices: what my Home Assistant app saves","A year of hourly EPEX prices for Austria, replayed for a Tesla and a water boiler: cheapest-hour charging costs 6.5 instead of 18.9 ct\u002FkWh, close to €590 a year, without blowing a 20 A fuse.","Cover: three bars comparing 18.9, 13.1 and 6.5 ct\u002FkWh for charging at 18:00, overnight and in the cheapest hours.",{"slug":720,"published":721,"minutes":722,"category":7,"tags":723,"keywords":730,"about":740,"sources":747,"cover":768,"og":769,"expertise":57,"locales":770,"lang":59,"title":771,"description":772,"coverAlt":773},"webmcp-agent-ready-website-guide","2026-09-27",10,[724,725,726,727,728,729],"WebMCP","Chrome","AI agents","Origin trial","Permissions Policy","JSON Schema",[724,731,732,733,734,735,736,737,738,739],"document.modelContext","registerTool","agent-ready website","WebMCP declarative API","WebMCP origin trial","WebMCP tools","tools Permissions Policy","how to expose website actions to AI agents","Chrome 149 WebMCP",[741,743,745],{"name":724,"url":742},"https:\u002F\u002Fgithub.com\u002Fwebmachinelearning\u002Fwebmcp",{"name":729,"url":744},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FJSON_Schema",{"name":728,"url":746},"https:\u002F\u002Fwww.w3.org\u002FTR\u002Fpermissions-policy-1\u002F",[748,751,754,757,760,763,765],{"title":749,"url":750},"Chrome for Developers: WebMCP (get started)","https:\u002F\u002Fdeveloper.chrome.com\u002Fdocs\u002Fai\u002Fwebmcp",{"title":752,"url":753},"Chrome for Developers: WebMCP Imperative API","https:\u002F\u002Fdeveloper.chrome.com\u002Fdocs\u002Fai\u002Fwebmcp\u002Fimperative-api",{"title":755,"url":756},"Chrome for Developers: WebMCP Declarative API","https:\u002F\u002Fdeveloper.chrome.com\u002Fdocs\u002Fai\u002Fwebmcp\u002Fdeclarative-api",{"title":758,"url":759},"Chrome for Developers: Join the WebMCP origin trial (9 Jun 2026)","https:\u002F\u002Fdeveloper.chrome.com\u002Fblog\u002Fai-webmcp-origin-trial",{"title":761,"url":762},"Chrome for Developers: 15 updates from Google I\u002FO 2026 (19 May 2026)","https:\u002F\u002Fdeveloper.chrome.com\u002Fblog\u002Fchrome-at-io26",{"title":764,"url":742},"WebMCP explainer: webmachinelearning\u002Fwebmcp",{"title":766,"url":767},"ChromeStatus: WebMCP feature entry","https:\u002F\u002Fchromestatus.com\u002Ffeature\u002F5117755740913664","\u002Fimages\u002Fblog\u002Fwebmcp-agent-ready-website-guide\u002Fcover.webp","\u002Fimages\u002Fblog\u002Fwebmcp-agent-ready-website-guide\u002Fog.jpg",[59,60,61],"WebMCP in practice: making a website agent-ready with declared tools","How WebMCP works in code: the declarative form attributes, document.modelContext.registerTool, tool annotations, the security gates, local testing and a checklist.","Two paths side by side: a form with toolname and tooldescription attributes that the browser turns into a schema, and a JavaScript tool object passed to document.modelContext.registerTool.",{"slug":775,"published":721,"minutes":674,"category":7,"tags":776,"keywords":780,"about":786,"sources":793,"cover":813,"og":814,"expertise":57,"locales":815,"lang":59,"title":816,"description":817,"coverAlt":818},"llms-txt-vs-markdown-content-negotiation",[777,778,726,779],"llms.txt","Content negotiation","Nuxt",[777,781,782,783,14,784,785,733],"markdown for agents","content negotiation","AI crawlers","Accept text\u002Fmarkdown header","llms-full.txt",[787,790],{"name":788,"url":789},"Markdown","https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FMarkdown",{"name":791,"url":792},"Hypertext Transfer Protocol","https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FHypertext_Transfer_Protocol",[794,797,798,801,804,807,810],{"title":795,"url":796},"llmstxt.org: The \u002Fllms.txt file (proposal, version 2)","https:\u002F\u002Fllmstxt.org\u002F",{"title":53,"url":54},{"title":799,"url":800},"Cloudflare changelog: Markdown for Agents (12 February 2026)","https:\u002F\u002Fdevelopers.cloudflare.com\u002Fchangelog\u002Fpost\u002F2026-02-12-markdown-for-agents\u002F",{"title":802,"url":803},"Cloudflare docs: Markdown for Agents","https:\u002F\u002Fdevelopers.cloudflare.com\u002Ffundamentals\u002Freference\u002Fmarkdown-for-agents\u002F",{"title":805,"url":806},"Ahrefs: 137K sites analyzed, 97% of llms.txt files never get read (June 2026)","https:\u002F\u002Fahrefs.com\u002Fblog\u002Fllmstxt-study\u002F",{"title":808,"url":809},"Checkly: The current state of content negotiation for AI agents (February 2026)","https:\u002F\u002Fwww.checklyhq.com\u002Fblog\u002Fstate-of-ai-agent-content-negotation\u002F",{"title":811,"url":812},"Suganthan: tracking Cloudflare Markdown for Agents on one site","https:\u002F\u002Fsuganthan.com\u002Fblog\u002Fcloudflare-markdown-for-agents\u002F","\u002Fimages\u002Fblog\u002Fllms-txt-vs-markdown-content-negotiation\u002Fcover.webp","\u002Fimages\u002Fblog\u002Fllms-txt-vs-markdown-content-negotiation\u002Fog.jpg",[59,60,61],"llms.txt vs Markdown content negotiation: what agents actually fetch","llms.txt is a proposal, Markdown content negotiation is a header. What AI agents fetch, what the logs show, and how to serve both from Nuxt and nginx.","Four stacked layers that serve AI agents: declared WebMCP tools, a Markdown copy of every page, a negotiated Markdown response, and an llms.txt index.",{"slug":820,"published":721,"minutes":674,"category":7,"tags":821,"keywords":827,"about":838,"sources":846,"cover":868,"og":869,"expertise":714,"locales":870,"lang":59,"title":871,"description":872,"coverAlt":873},"nuxt-llm-features-ai-sdk-streaming",[779,822,823,824,825,826],"AI SDK","Streaming","Structured output","Tool approval","Nitro",[828,829,830,831,832,833,834,835,836,837],"Nuxt AI","AI SDK Vue","Nuxt server route LLM","streaming chat Nuxt","useChat @ai-sdk\u002Fvue","structured output JSON Schema limits","human in the loop tool approval","Nitro API route model key","AI Act Article 50 chatbot","stopWhen isStepCount",[839,841,843],{"name":779,"url":840},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FNuxt",{"name":822,"url":842},"https:\u002F\u002Fai-sdk.dev",{"name":844,"url":845},"Zod","https:\u002F\u002Fzod.dev",[847,850,853,856,859,862,865],{"title":848,"url":849},"AI SDK docs: Vue.js (Nuxt) quickstart","https:\u002F\u002Fai-sdk.dev\u002Fdocs\u002Fgetting-started\u002Fnuxt",{"title":851,"url":852},"AI SDK docs: Generating structured data","https:\u002F\u002Fai-sdk.dev\u002Fdocs\u002Fai-sdk-core\u002Fgenerating-structured-data",{"title":854,"url":855},"AI SDK docs: Tool approvals","https:\u002F\u002Fai-sdk.dev\u002Fdocs\u002Fagents\u002Ftool-approvals",{"title":857,"url":858},"Claude API docs: Structured outputs","https:\u002F\u002Fplatform.claude.com\u002Fdocs\u002Fen\u002Fbuild-with-claude\u002Fstructured-outputs",{"title":860,"url":861},"AI SDK docs: Telemetry","https:\u002F\u002Fai-sdk.dev\u002Fdocs\u002Fai-sdk-core\u002Ftelemetry",{"title":863,"url":864},"EU AI Act, Article 50: Transparency obligations","https:\u002F\u002Fartificialintelligenceact.eu\u002Farticle\u002F50\u002F",{"title":866,"url":867},"Faegre Drinker: Commission confirms the Transparency Code of Practice (Jul 2026)","https:\u002F\u002Fwww.faegredrinker.com\u002Fen\u002Finsights\u002Fpublications\u002F2026\u002F7\u002Feu-ai-act-commission-confirms-transparency-code-of-practice-as-adequate-and-publishes-final-version-of-its-guidelines-on-transparency-obligations","\u002Fimages\u002Fblog\u002Fnuxt-llm-features-ai-sdk-streaming\u002Fcover.webp","\u002Fimages\u002Fblog\u002Fnuxt-llm-features-ai-sdk-streaming\u002Fog.jpg",[59,60,61],"Shipping LLM features in Nuxt: streaming, structured output, tool approval","Nuxt AI end to end: a server route that holds the API key, streaming parts, strict structured output, tools needing approval, errors and Article 50.","A sequence from the browser to the Nitro route to the model provider and a tool, and a component tree of the chat panel, message list and part renderer.",1790676829939]