As a programmer who deals with code daily, I have recently noticed some subtle changes in the web traffic ecosystem.
From SEO to GEO: My Blog System AI Search Optimization and Implementation Practice
Published: 2026-06-19 (a month ago)
GEO

As a programmer who deals with code daily, I have recently noticed some subtle changes in the web traffic ecosystem.

In the past, when we built personal blogs or technical websites, we stared at Google or Baidu index counts every day, researching how to improve PageRank through keyword matching and external backlinks.

But now, more and more friends do not flip through search engines page by page when encountering technical difficulties. Instead, they directly open ChatGPT, Kimi, Perplexity, or use Google's AI Overviews (AI Search) to ask: "What is Go's GMP model all about?"

At this moment, AI will compile a plain, easy-to-understand answer for you in a few seconds, and thoughtfully tag it with several colorful little bubbles (citation source links) nearby. When clicked, it turns out to be a developer's technical blog.

This technique of having AI crawl your content and cite your website as a "bibliography" in its response is the recently popularized GEO (Generative Engine Optimization).

A while ago, I spent some effort to carry out a thorough modern refactoring of my blog system. Taking my actual project (Nuxt 4 + Go/Gin) as an example, this article will walk you through, in plain language and code, how I implemented SEO and GEO optimization.


1. Why Can Large Language Models Find Your Web Pages? A Plain Talk on the RAG Mechanism

Before writing code, let's spend a minute understanding the principles of AI search. How does AI know what you wrote on your website?

Current AI search systems (such as Perplexity) generally rely on a technology called RAG (Retrieval-Augmented Generation). Simply put, its workflow looks like this:

  1. User Question: The user inputs "How to deploy Elasticsearch 8 with Docker".
  2. Information Gathering: The AI search engine pretends to be an ordinary user and uses traditional search engines to download the top dozens of web pages (this requires your web page to be crawl-friendly, rather than a dynamically loaded empty shell).
  3. LLM Synthesis: The AI reads these dozens of pages, extracts the core value, and synthesizes it into a plain explanation for the user.
  4. Citing Reference Links: To prevent making things up (hallucination), the AI attaches the URLs of the contributing web pages next to the response as "citation sources".

Therefore, the core purpose of GEO optimization is to reduce the cost of AI "gathering and reading information", making it feel that reading your article is the easiest and most reliable option.


2. Core Implementation: My Four-Step Redesign Plan

To make AI find our website highly attractive, I performed the following four upgrades to my blog system.


Step 1: Dump SPA, Embrace SSR and Semantic URLs (Slug)

If your blog is still a pure Single Page Application (SPA, such as a static page built with pure Vue or React), it is basically out of the game in the AI era. This is because an SPA initially contains only an empty shell like <div id="app"></div>, and all content relies on the browser executing JS for dynamic rendering. AI crawlers do not have the patience to wait for your JS to load; when they see an empty page, they simply turn away.

Therefore, the first thing I did was refactor the blog to use Nuxt 4's Server-Side Rendering (SSR). This way, when a crawler visits, the server serves the fully rendered HTML with full text content directly.

Additionally, we need to restructure our URLs.

  • Old URL: https://yoursite.com/article/32 (Cold numbers, illegible to both crawlers and humans)
  • New URL: https://yoursite.com/article/32-online-platform-personalized-recommendation (Contains English keywords with clear semantics)

🛠️ Code Implementation in My Project:

On the frontend, I wrote a utility class to automatically convert article links. If an article has an English title, it is converted with priority; otherwise, Chinese special characters are filtered out and connected with hyphens -:

typescript Copy
// utils/slug.ts

/**
 * Convert Chinese/English titles to flat URL Slugs complying with SEO/GEO standards
 * For example, converting "Docker deploy ES 8.x" into "docker-deployment-es-8x"
 */
export function getSlug(title: string, titleEn?: string): string {
    const baseText = titleEn || title;
    
    return baseText
        .toLowerCase()
        // Filter out miscellaneous symbols except English letters, numbers, Chinese characters, spaces, and hyphens
        .replace(/[^a-z0-9\u4e00-\u9fa5\s-]/g, '') 
        // Replace consecutive spaces with a single hyphen
        .replace(/\s+/g, '-')
        // Prevent consecutive multiple hyphens, e.g., turning --- into -
        .replace(/-+/g, '-')
        .trim();
}

/**
 * Assemble a complete article link
 */
export function getArticleLink(article: { id: number; title: string; title_en?: string }): string {
    const slug = getSlug(article.title, article.title_en);
    return `/article/${article.id}-${slug}`;
}

On the backend (I am using Go's Gin framework), the route is designed as /article/:id_slug. When parsing parameters, it is actually very simple and straightforward —— we only take the primary key ID at the beginning to query the database, ignoring the long trailing string of English keywords:

go Copy
// Example of parameter parsing in backend Gin route controller
func GetArticleDetail(c *gin.Context) {
    // E.g., receiving "32-online-platform-personalized-recommendation"
    idSlug := c.Param("id_slug")
    
    // Split by "-", and take the first part "32"
    idStr := strings.Split(idSlug, "-")[0]
    id, err := strconv.Atoi(idStr)
    if err != nil {
        c.JSON(400, gin.H{"error": "Invalid article ID"})
        return
    }
    
    // Still use the fast primary key ID to query the database, ensuring performance
    article := queryArticleFromDB(id)
    c.JSON(200, gin.H{"code": 0, "data": article})
}

Step 2: Present Your Business Card —— Inject JSON-LD Structured Metadata

When large language model crawlers enter your web page, if they have to guess from the HTML paragraphs which part is the main text, who the author is, and when it was published, it is not only laborious but also prone to errors.

We can proactively hand them a "business card" —— JSON-LD. This packages the core information of the web page into a standardized piece of JSON code, hidden inside the HTML header (invisible to users, but instantly understood by crawlers).

🛠️ Code Implementation in My Project:

In Nuxt 4's article details page, we utilize useHead to dynamically construct BlogPosting metadata conforming to the Schema.org standard:

vue Copy
<!-- pages/article/[id].vue -->
<script setup lang="ts">
import { computed } from 'vue'

const { data: article } = await useAsyncData(`article-${id}`, () => fetchArticle(id))

// Dynamically construct AI's favorite JSON-LD structured data
const jsonLd = computed(() => {
    if (!article.value) return null
    return {
        '@context': 'https://schema.org',
        '@type': 'BlogPosting',
        'headline': article.value.title,
        'description': article.value.abstract,
        'image': article.value.banner_path ? [article.value.banner_path] : [],
        'datePublished': article.value.created_at,
        'author': {
            '@type': 'Person',
            'name': 'Uncle Sam'
        },
        'publisher': {
            '@type': 'Organization',
            'name': "Sam's Tech Blog",
            'logo': {
                '@type': 'ImageObject',
                'url': 'https://yoursite.com/logo.png' // Replace with your actual logo URL
            }
        }
    }
})

// Write it into the HTML head
useHead({
    title: () => `${article.value?.title} - Sam's Blog`,
    script: [
        {
            type: 'application/ld+json',
            // 💡 Note: Must write using innerHTML, not inside children, otherwise characters will be escaped and cause errors during SSR (Server-Side Rendering)
            innerHTML: () => jsonLd.value ? JSON.stringify(jsonLd.value) : ''
        }
    ]
})
</script>

Once this code takes effect, Google will add thumbnails, publication time, and author avatars (Rich Snippets) to your articles in search results, and AI can extract all properties of this article with 100% accuracy.


Step 3: Automatically Update the Website "Registry" —— Dynamic Multi-Source Sitemap

Simply writing web pages is not enough; we must let crawlers know when we have new articles updated. This requires sitemap.xml (Sitemap). In the past, many people wrote an XML file manually, but for blogs that update frequently, this is too unrealistic.

🛠️ Code Implementation in My Project:

I integrated the @nuxtjs/sitemap module and wrote a server-side API to automatically serve the latest Slug links daily or whenever a new article is published.

First, declare the Sitemap data source in nuxt.config.ts:

typescript Copy
// nuxt.config.ts
export default defineNuxtConfig({
  modules: ['@nuxtjs/sitemap'],
  
  sitemap: {
    // Dynamic article URL data source points to our server-side route
    sources: ['/api/__sitemap__/urls'],
    // Exclude admin backend pages that do not need indexing
    exclude: ['/login', '/admin/**']
  }
})

Next, create /server/api/__sitemap__/urls.ts on the server-side. This API runs quietly in the background, fetching the full article list by sending requests to the Go backend, and generating the URL array dedicated for the sitemap:

typescript Copy
// server/api/__sitemap__/urls.ts
import { getArticleLink } from '~/utils/slug'

interface ApiResponse {
    code: number
    data: {
        list: Array<{ id: number; title: string; title_en: string; created_at: string }>
    }
}

export default defineSitemapEventHandler(async () => {
    const config = useRuntimeConfig()
    const backendBase = config.serverApiUrl || 'http://127.0.0.1:8000'

    try {
        // Request Go API to get all articles on the site
        const res: ApiResponse = await $fetch(`${backendBase}/api/articles?page=1&limit=1000`)
        if (res.code !== 0) return []

        // Convert the article list into the format required by sitemap
        return res.data.list.map(article => ({
            // Call the getArticleLink utility written in Step 1 to ensure URL consistency
            loc: getArticleLink({
                id: article.id,
                title: article.title,
                title_en: article.title_en,
            }),
            lastmod: article.created_at,
            changefreq: 'weekly' as const, // Tell the crawler that this page is updated roughly weekly
            priority: 0.9                  // Priority weight: 0.0 ~ 1.0
        }))
    } catch (e) {
        console.error('Failed to get article list for Sitemap:', e)
        return []
    }
})

Once this step is done, when we access http://yoursite.com/sitemap.xml, we can see a clean, real-time map of all links across the site, enabling AI crawlers to crawl our entire site clearly following this "registry".


Step 4: Reorganize Robots Rules to Welcome AI Crawlers

Some traditional robots.txt files block too many paths. We need to explicitly state which LLM crawlers are allowed to crawl, which is also the simplest but most important part of GEO optimization.

🛠️ Code Implementation in My Project:

Edit and optimize public/robots.txt, giving priority green light to mainstream AI crawlers:

ini Copy
User-agent: *
Allow: /
Disallow: /login
Disallow: /admin

# ---- OpenAI / ChatGPT Search Dedicated Green Card ----
User-agent: GPTBot
Allow: /

# ---- Google Gemini / Google AI Search ----
User-agent: Google-Extended
Allow: /

# ---- Anthropic Claude Dedicated Crawler ----
User-agent: ClaudeBot
Allow: /

# ---- Perplexity AI Search Crawler ----
User-agent: PerplexityBot
Allow: /

# Tell them where the sitemap is
Sitemap: https://yoursite.com/sitemap.xml

3. How to Verify If What We Did Is Effective?

After the redesign goes live, you can test the results using the following two methods:

  1. Test JSON-LD Format: Use Google's official Rich Results Test Tool and input your article link. If it shows "No errors found" and recognizes the complete BlogPosting schema, your metadata is successful.
  2. Test AI Citations: Try searching for technical difficulties explained in your article using natural language on Perplexity. You will find that when AI organizes the answers, it will display your article as a small bubble card near the answer, linking directly to our beautifully structured Slug URL.

4. Summary and Recommendations

Today, as artificial intelligence reshapes the Internet, "content is king" remains an immutable truth, but "how to make AI read content better" has become a core skill for programmers.

The implementation of GEO is essentially about reducing the loss of information during transmission, crawling, and understanding. By using Server-Side Rendering (SSR) to ensure content is immediately visible, using JSON-LD to reassure the LLMs, and using semantic URLs and dynamic Sitemaps for easy indexing.

If you are also building or upgrading your personal tech blog or content-based platform, it is recommended to include these items in your refactoring checklist. Technology should not just be a self-entertainment; capturing the traffic entry point in this AI era and letting LLMs drive traffic for us is the true first step towards "monetizing technology".