🔵

GEO for WordPress: Complete Implementation Guide

beginner

Published April 18, 2026 · Updated May 13, 2026

WordPress sites with AI optimization plugins get 3.8x more AI citations (Stanford HAI, Q4 2024). In 2025: MAIO plugin tracks citations in ChatGPT, Gemini, Perplexity, and Claude. Yoast SEO and RankMath both updated for AI Overviews. Add OAI-SearchBot and PerplexityBot to your robots.txt Allow list. No mainstream plugin handles llms.txt yet — use a PHP snippet.

GEO for WordPress: Complete Implementation Guide

WordPress implements GEO via Yoast SEO (recommended) for automatic meta tags and schema generation, or via custom functions.php code. Add wp_head() hooks for article dates and Open Graph. WordPress already generates static HTML, making it natively compatible with AI crawlers.

WordPress has a natural advantage for GEO: it renders server-side HTML by default, so AI crawlers can read your content without JavaScript. The main work is ensuring complete meta tags, schema markup, and proper robots.txt configuration.

Yoast SEO Premium and RankMath Pro generate all required GEO meta tags and schema automatically as of 2026. This is the fastest path to GEO compliance.

What Yoast SEO handles automatically:

  • Title and meta description
  • Open Graph tags (og:type, og:title, og:description, og:image, og:url)
  • article:published_time and article:modified_time
  • JSON-LD Article schema with publisher and dates
  • BreadcrumbList schema
  • Organization schema on homepage
  • FAQPage schema via Gutenberg FAQ block

Yoast SEO configuration:

  1. Install Yoast SEO (free) or Yoast SEO Premium
  2. Go to SEO → Search Appearance → configure site name and logo
  3. Go to SEO → Social → enable Open Graph
  4. For each post: fill in the SEO title and meta description in the Yoast panel
  5. For FAQPage schema: use the FAQ block in Gutenberg editor

Option 2: Custom functions.php

For developers who prefer full control, add this to your theme’s functions.php:

<?php
// functions.php

/**
 * Add complete GEO meta tags to singular posts and pages
 */
function geo_meta_tags() {
  if (!is_singular()) return;

  $published = get_the_date('c');
  $modified  = get_the_modified_date('c');
  $url       = get_permalink();
  $desc      = wp_strip_all_tags(get_the_excerpt() ?: get_bloginfo('description'));
  $image     = get_the_post_thumbnail_url(null, 'large');
  $author_id = get_post_field('post_author');
  $author_url = get_author_posts_url($author_id);

  echo '<meta name="author" content="' . esc_attr(get_the_author_meta('display_name', $author_id)) . '">' . "\n";
  echo '<link rel="canonical" href="' . esc_url($url) . '">' . "\n";

  // Open Graph
  echo '<meta property="og:type" content="article">' . "\n";
  echo '<meta property="og:title" content="' . esc_attr(get_the_title()) . '">' . "\n";
  echo '<meta property="og:description" content="' . esc_attr($desc) . '">' . "\n";
  echo '<meta property="og:url" content="' . esc_url($url) . '">' . "\n";
  echo '<meta property="og:site_name" content="' . esc_attr(get_bloginfo('name')) . '">' . "\n";
  echo '<meta property="og:locale" content="' . esc_attr(str_replace('-', '_', get_locale())) . '">' . "\n";
  if ($image) {
    echo '<meta property="og:image" content="' . esc_url($image) . '">' . "\n";
  }

  // Article dates (critical recency signal)
  echo '<meta property="article:published_time" content="' . esc_attr($published) . '">' . "\n";
  echo '<meta property="article:modified_time" content="' . esc_attr($modified) . '">' . "\n";
  echo '<meta property="article:author" content="' . esc_url($author_url) . '">' . "\n";

  // Article tags
  $tags = get_the_tags();
  if ($tags) {
    foreach ($tags as $tag) {
      echo '<meta property="article:tag" content="' . esc_attr($tag->name) . '">' . "\n";
    }
  }
}
add_action('wp_head', 'geo_meta_tags', 5);


/**
 * Add JSON-LD Article schema
 */
function geo_json_ld() {
  if (!is_singular()) return;

  $schema = [
    '@context'  => 'https://schema.org',
    '@type'     => 'Article',
    'headline'  => get_the_title(),
    'description' => wp_strip_all_tags(get_the_excerpt()),
    'author'    => [
      '@type' => 'Person',
      'name'  => get_the_author(),
      'url'   => get_author_posts_url(get_the_author_meta('ID')),
    ],
    'publisher' => [
      '@type' => 'Organization',
      'name'  => get_bloginfo('name'),
      'logo'  => [
        '@type' => 'ImageObject',
        'url'   => get_site_icon_url(512) ?: get_bloginfo('url') . '/logo.png',
      ],
    ],
    'datePublished'    => get_the_date('c'),
    'dateModified'     => get_the_modified_date('c'),
    'mainEntityOfPage' => [
      '@type' => 'WebPage',
      '@id'   => get_permalink(),
    ],
  ];

  $thumbnail = get_the_post_thumbnail_url(null, 'large');
  if ($thumbnail) {
    $schema['image'] = $thumbnail;
  }

  echo '<script type="application/ld+json">'
    . wp_json_encode($schema, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)
    . '</script>' . "\n";
}
add_action('wp_head', 'geo_json_ld');


/**
 * Add Organization schema on homepage
 */
function geo_organization_schema() {
  if (!is_front_page()) return;

  $schema = [
    '@context'    => 'https://schema.org',
    '@type'       => 'Organization',
    'name'        => get_bloginfo('name'),
    'url'         => get_bloginfo('url'),
    'description' => get_bloginfo('description'),
    'logo'        => get_site_icon_url(512) ?: get_bloginfo('url') . '/logo.png',
  ];

  echo '<script type="application/ld+json">'
    . wp_json_encode($schema, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)
    . '</script>' . "\n";
}
add_action('wp_head', 'geo_organization_schema');

robots.txt

WordPress generates robots.txt dynamically. To add AI crawlers, use the robots_txt filter:

// functions.php
function geo_robots_txt($output, $public) {
  if ($public) {
    $output .= "\nUser-agent: GPTBot\nAllow: /\n";
    $output .= "\nUser-agent: OAI-SearchBot\nAllow: /\n";
    $output .= "\nUser-agent: ClaudeBot\nAllow: /\n";
    $output .= "\nUser-agent: Claude-User\nAllow: /\n";
    $output .= "\nUser-agent: Claude-SearchBot\nAllow: /\n";
    $output .= "\nUser-agent: PerplexityBot\nAllow: /\n";
    $output .= "\nUser-agent: Google-Extended\nAllow: /\n";
    $output .= "\nSitemap: " . get_bloginfo('url') . "/llms.txt\n";
  }
  return $output;
}
add_filter('robots_txt', 'geo_robots_txt', 10, 2);

llms.txt via WordPress Page or Plugin

Create a custom endpoint for llms.txt:

// functions.php
function geo_llms_txt() {
  if ($_SERVER['REQUEST_URI'] === '/llms.txt') {
    header('Content-Type: text/plain');
    $site_name = get_bloginfo('name');
    $site_url = get_bloginfo('url');
    $description = get_bloginfo('description');

    echo "# {$site_name}\n";
    echo "> {$description}\n\n";
    echo "## Main Content\n";

    // Get recent posts
    $posts = get_posts(['numberposts' => 20, 'post_status' => 'publish']);
    foreach ($posts as $post) {
      $excerpt = wp_strip_all_tags(get_the_excerpt($post));
      echo "- [{$post->post_title}](" . get_permalink($post) . "): {$excerpt}\n";
    }

    echo "\n## About\n";
    echo "- [About Us]({$site_url}/about): Team and credentials\n";
    exit;
  }
}
add_action('init', 'geo_llms_txt');

XML Sitemap

Use the Yoast SEO sitemap (enabled by default) or install a dedicated sitemap plugin like “Simple Sitemap” or “Google XML Sitemaps”. All generate <lastmod> dates automatically from WordPress post modification times.

WordPress REST API provides modified timestamps — sitemap plugins use these for <lastmod>.

GEO Checklist for WordPress

  • Yoast SEO or RankMath installed and configured (recommended path)
  • OR: geo_meta_tags() and geo_json_ld() added to functions.php
  • robots_txt filter adds GPTBot, ClaudeBot, PerplexityBot, Google-Extended
  • llms.txt endpoint created or Yoast generates it
  • XML sitemap active with lastmod dates (Yoast or plugin)
  • Yoast SEO: Social settings → Open Graph enabled
  • Each post has SEO title and meta description filled in
  • Images have alt text (improves AI comprehension)
  • Inverted pyramid: answer in first paragraph of each post
  • Core Web Vitals: LCP < 2.5s, INP < 200ms, CLS < 0.1

GEO-Specific WordPress Plugins (2025)

MAIO — AI Citation Tracking

MAIO tracks how often your site is cited in ChatGPT, Gemini, Perplexity, and Claude responses. Available at WordPress.org:

Dashboard → Plugins → Add New → Search “MAIO AI GEO”

Provides a citation analytics dashboard inside WordPress admin.

Yoast SEO 2025 Updates

Yoast updated schema output for AI Overviews:

  • Improved Article, FAQPage, and HowTo schema support
  • author.sameAs links to LinkedIn/Twitter generated automatically
  • E-E-A-T signals guidance in the readability analysis

RankMath 2025 Updates

RankMath introduced AI search optimization features:

  • AI Overviews readiness score per post
  • Automatic FAQPage schema from FAQ blocks
  • GEO-specific recommendations alongside traditional SEO

AI Crawlers in robots.txt

Update your WordPress robots.txt (via Yoast → Tools → File Editor or directly at your-domain.com/robots.txt):

User-agent: GPTBot
Allow: /

User-agent: OAI-SearchBot
Allow: /

User-agent: PerplexityBot
Allow: /

User-agent: ClaudeBot
Allow: /

User-agent: Google-Extended
Allow: /

Check: Many caching and security plugins (Wordfence, WP Super Cache) add blanket bot restrictions. Verify your live robots.txt to ensure AI bots are not blocked.

Generating llms.txt in WordPress

No mainstream plugin handles llms.txt in WordPress yet (mid-2025). Add this PHP snippet to functions.php:

add_action('init', function() {
  add_rewrite_rule('^llms\.txt$', 'index.php?llms_txt=1', 'top');
});

add_filter('query_vars', function($vars) {
  $vars[] = 'llms_txt';
  return $vars;
});

add_action('template_redirect', function() {
  if (!get_query_var('llms_txt')) return;
  header('Content-Type: text/plain');
  echo "# " . get_bloginfo('name') . "\n\n";
  echo "> " . get_bloginfo('description') . "\n\n";
  echo "## Posts\n\n";
  $posts = get_posts(['numberposts' => 50, 'post_status' => 'publish']);
  foreach ($posts as $post) {
    echo "- [{$post->post_title}](" . get_permalink($post) . ")\n";
  }
  exit;
});

After adding: Settings → Permalinks → Save Changes (flushes rewrite rules).