← All posts

I Ran a GEO Audit on My Own Portfolio. Here's What I Fixed.

My portfolio scored 68/100 on a GEO audit. To fix AI citation issues, I added JSON-LD schemas, an llms.txt, and rewrote prose to lead with facts.

I Ran a GEO Audit on My Own Portfolio. Here's What I Fixed.

I ran an SEO/GEO audit on my own portfolio site and the results were humbling. Classical SEO score: 74/100. GEO score: 68/100. The site looked good, loaded fast, and had decent meta tags — but an AI answer engine couldn't confidently quote it. No named entities. No machine-readable summary. Prose full of vague descriptors instead of citable facts. I spent a day fixing that, and this post documents exactly what I changed.

What GEO Actually Is

Generative Engine Optimization is the practice of structuring content so AI answer engines — ChatGPT, Perplexity, Gemini, Google AI Overviews — can parse, trust, and cite it. Classic SEO optimizes for ranked blue links. GEO optimizes for being quoted in the answer. The distinction matters because the citation mechanics are different: a search engine ranks pages by authority signals, while an answer engine extracts claims and attributes them to a source it can verify. If your content is vague, unstructured, or lacks entity signals, the engine will paraphrase a competitor who was more explicit.

GEO doesn't replace SEO. It's a layer on top. Most GEO techniques are also good SEO hygiene — structured data, semantic HTML, factual prose. The difference is intent: you're writing for a system that needs to answer a question in one paragraph, not drive a click.

The Audit Numbers

I ran a structured AI audit against my Next.js 15 App Router site deployed on Vercel. The model evaluated both classical SEO signals and GEO-specific criteria separately. Classical SEO came back at 74/100 — reasonable, but losing points on structured data coverage and thin metadata on blog posts. GEO came back at 68/100 — the bigger gap, and the more interesting problem.

The GEO deductions were consistent: no Person entity with disambiguating links, no machine-readable site summary, prose that described work in adjectives rather than facts, and FAQ content buried in paragraph form where an engine couldn't extract a clean answer. None of these are hard to fix. They're just easy to skip when you're focused on shipping features.

JSON-LD: Explicit Entities

JSON-LD structured data is the primary mechanism for telling an answer engine who you are, what you do, and why a claim on your site is attributable to a named person or organization. I added three schema types to the site layout — Person, WebSite, ProfessionalService — and two to every blog post — BlogPosting and BreadcrumbList.

The BlogPosting schema is where the GEO value is most concentrated. Here's a trimmed version of what's injected on each post:

{
  "@context": "https://schema.org",
  "@type": "BlogPosting",
  "headline": "I Ran a GEO Audit on My Own Portfolio. Here's What I Fixed.",
  "description": "A practical walkthrough of GEO fixes on a Next.js 15 site, from JSON-LD to llms.txt.",
  "datePublished": "2026-06-02",
  "dateModified": "2026-06-02",
  "wordCount": 1100,
  "isAccessibleForFree": true,
  "mainEntityOfPage": {
    "@type": "WebPage",
    "@id": "https://klawsfx.com/blog/geo-audit-nextjs-portfolio"
  },
  "author": {
    "@type": "Person",
    "name": "Kyriakos Kaitezidis",
    "url": "https://klawsfx.com",
    "sameAs": [
      "https://github.com/kykakaite"
    ]
  }
}

The sameAs array is the key GEO signal. It gives the engine corroborating sources to verify the author identity. Without it, "Kyriakos Kaitezidis wrote this" is an unverified claim. With it, the engine can cross-reference the linked profiles and treat the authorship as established.

llms.txt: A Machine-Readable Site Summary

llms.txt is a plain-text, structured file served at the site root that gives LLMs a concise, accurate summary of what the site is and who runs it. Think of it as robots.txt but for answer engines — not access control, but orientation.

My /llms.txt leads with a definitional summary, then lists services, stack, and contact in terse, attributable lines:

# Kyriakos Kaitezidis

> Full-stack developer based in Xanthi, Greece. I build complete websites
> and web apps — frontend, backend, database, deployment — typically shipped
> in 3–5 days. Fixed-price quotes. Direct.

## Stack
- Frontend: React, Next.js, Astro, TypeScript, Tailwind, GSAP, R3F/WebGL
- Backend: Node.js, Express, tRPC, GraphQL, REST, WebSockets
- Database: PostgreSQL, Prisma, Drizzle, Neon, Redis
- Deploy: Vercel, Netlify, Cloudflare, Docker, GitHub Actions

## Contact
- Email: contact@klawsfx.com
- Website: https://klawsfx.com
- GitHub: https://github.com/kykakaite

When an engine crawls the site or a user asks "who is Kyriakos Kaitezidis," this file gives it a ground truth it can cite directly. The format is intentionally terse and factual — no marketing language, just attributable statements.

Definitional Sentences and Fact-Dense Prose

Answer engines extract claims by looking for self-contained, declarative sentences they can lift without surrounding context. A sentence like "Kyriakos Kaitezidis is a full-stack developer based in Xanthi who ships complete websites and web apps in 3–5 days" is citable. A sentence like "I create immersive digital experiences at the intersection of art and technology" is not — it's adjectives without facts.

I rewrote the site's hero text, About section, and project descriptions to lead with definitional sentences. Every section now opens with a claim a machine can verify or attribute. This is the same technique used in Wikipedia leads and press releases: state the key fact first, then elaborate.

The same rule applies at the section level. Each ## heading in this post opens with a declarative sentence that defines the topic. An engine reading this post can extract "Generative Engine Optimization is the practice of structuring content so AI answer engines can parse, trust, and cite it" without reading the surrounding paragraphs.

Semantic HTML and Clean Metadata

Structured data and good prose only work if the underlying HTML is parseable. I audited the site for heading hierarchy, canonical URLs, OpenGraph completeness, and image alt text. Specific fixes:

None of this is novel. But GEO amplifies the cost of skipping it — an engine that can't parse your structure won't quote you even if your content is excellent.

The Result

After shipping these changes, the same audit model re-scored the site. The improvements were measurable across both dimensions. More importantly, the site now has named entities with corroborating links, a machine-readable summary at a known URL, and prose that leads with facts. Whether or not a specific engine quotes this site on any given query, the content is now structured to support it.

The underlying principle: write for the answer, not the click. AI engines are trying to serve one correct, attributable answer. Give them the raw material to do that confidently, and your content becomes a source rather than a reference.

FAQ

The FAQ format itself is a GEO technique. Each answer below is written as a self-contained response that an AI engine can extract and cite without reading the surrounding post.

What is Generative Engine Optimization (GEO)? Generative Engine Optimization (GEO) is the practice of structuring web content so that AI answer engines — including ChatGPT, Perplexity, Gemini, and Google AI Overviews — can parse, trust, and cite it as a source. Unlike classical SEO, which targets ranked search results, GEO targets direct quotation in AI-generated answers.

What is an llms.txt file? An llms.txt file is a plain-text document served at a site's root URL that provides a structured, machine-readable summary of the site's content, author, and purpose for large language models. It functions similarly to robots.txt in its placement and convention, but instead of controlling crawler access it provides orientation — who runs the site, what it covers, and where key resources are.

Why does JSON-LD sameAs matter for GEO? The sameAs property in a Person schema links the author's identity to corroborating external profiles such as GitHub or LinkedIn, giving AI engines cross-references they can use to verify authorship before attributing a claim. Without it, the author name is an unverified string; with it, it becomes a resolvable entity the engine can treat as established.

Does GEO require a different site architecture? GEO does not require a new architecture — it is applied as a layer on top of an existing site through structured data, metadata, and writing conventions. On a Next.js App Router site, the primary implementation points are the root layout (for Person, WebSite, ProfessionalService JSON-LD), individual post pages (for BlogPosting JSON-LD), a static llms.txt in the public/ directory, and prose rewritten to lead with definitional sentences.