25% vs. previous period)', 'Check Manual Actions and Security Issues — flag anything that is not "No issues detected"', 'Confirm the verified property matches the canonical URL (http vs https, www vs non-www) actually serving', ]; } public function watch_outs(): array { return [ 'Verification meta tags can be added by SEO plugins (Yoast, Rank Math, AIOSEO) or by Site Kit — the presence of a tag does not tell you which Google account owns the property', 'A missing tag does not mean the site is unverified — DNS TXT and file-based verification are equally valid and not visible from the front end', ]; } public function autocheck(array $session_state): array { $f = []; $home = home_url('/'); // 1. Homepage HTML — look for verification meta tag(s). $resp = wp_remote_get($home, ['timeout' => 8, 'redirection' => 3]); if (is_wp_error($resp)) { $f[] = $this->finding('fetch', 'warn', 'Homepage fetch', 'failed', $resp->get_error_message()); } else { $body = (string) wp_remote_retrieve_body($resp); $tokens = []; if (preg_match_all('/]+name=["\']google-site-verification["\'][^>]*content=["\']([^"\']+)["\']/i', $body, $m)) { $tokens = $m[1]; } $f[] = $this->finding( 'verification_meta', $tokens ? 'ok' : 'info', 'GSC verification meta tag', $tokens ? count($tokens) . ' present' : 'not found on homepage', $tokens ? 'Token(s): ' . implode(', ', array_map(fn($t) => substr($t, 0, 12) . '…', $tokens)) : 'Absent tag is not a problem if verification is via DNS TXT or an uploaded HTML file — confirm in Search Console.' ); } // 2. Sitemap reachability. Try common locations, take the first that responds 200. $candidates = [ 'wp-sitemap.xml', // WP core (5.5+) 'sitemap_index.xml', // Yoast/Rank Math default 'sitemap.xml', ]; $found_sitemap = null; foreach ($candidates as $rel) { $url = trailingslashit($home) . $rel; $head = wp_remote_head($url, ['timeout' => 5, 'redirection' => 2]); if (is_wp_error($head)) continue; $code = (int) wp_remote_retrieve_response_code($head); if ($code === 200) { $found_sitemap = $url; break; } } if ($found_sitemap) { $f[] = $this->finding( 'sitemap', 'ok', 'Sitemap', $found_sitemap, 'Reachable — confirm this URL is the one submitted in Search Console.' ); } else { $f[] = $this->finding( 'sitemap', 'warn', 'Sitemap', 'none of the common URLs responded', 'Tried: ' . implode(', ', $candidates) . '. A missing sitemap doesn\'t prevent indexing but Search Console will show a fetch error.' ); } // 3. robots.txt — surface if it blocks Googlebot from the site root. $robots_url = trailingslashit($home) . 'robots.txt'; $rob = wp_remote_get($robots_url, ['timeout' => 5, 'redirection' => 2]); if (is_wp_error($rob)) { $f[] = $this->finding('robots', 'info', 'robots.txt', 'unreachable', $rob->get_error_message()); } else { $code = (int) wp_remote_retrieve_response_code($rob); if ($code !== 200) { $f[] = $this->finding('robots', 'info', 'robots.txt', 'HTTP ' . $code, 'WordPress serves a virtual robots.txt by default; a non-200 response may indicate a redirect or a plugin intercepting it.'); } else { $rb = (string) wp_remote_retrieve_body($rob); $blocks_root = $this->robots_blocks_root($rb); $f[] = $this->finding( 'robots', $blocks_root ? 'bad' : 'ok', 'robots.txt', $blocks_root ? 'blocks Googlebot from /' : 'does not block /', $blocks_root ? 'A "Disallow: /" applying to * or Googlebot will prevent indexing. Check Settings → Reading for "Discourage search engines".' : '' ); } } // 4. "Discourage search engines" WP setting — hard block on indexing. if ((int) get_option('blog_public') === 0) { $f[] = $this->finding( 'blog_public', 'bad', 'Search engine indexing', 'discouraged (Settings → Reading)', 'The "Discourage search engines from indexing this site" checkbox is on. This should be off on production.' ); } else { $f[] = $this->finding('blog_public', 'ok', 'Search engine indexing', 'allowed', ''); } // 5. Site Kit — the WP-side surface for Search Console data. Informational. if (!function_exists('is_plugin_active')) require_once ABSPATH . 'wp-admin/includes/plugin.php'; $sitekit = is_plugin_active('google-site-kit/google-site-kit.php'); $f[] = $this->finding( 'site_kit', $sitekit ? 'ok' : 'info', 'Site Kit by Google', $sitekit ? 'active' : 'not active', $sitekit ? 'GSC data may be visible on the WP dashboard.' : '' ); return $f; } /** Parse robots.txt and decide whether Googlebot (or *) is disallowed from /. */ private function robots_blocks_root(string $robots): bool { $lines = preg_split('/\r?\n/', $robots) ?: []; $current_agents = []; $groups = []; // agent => [disallow rules] foreach ($lines as $line) { $line = trim(preg_replace('/#.*$/', '', $line)); if ($line === '') { $current_agents = []; continue; } if (preg_match('/^user-agent:\s*(.+)$/i', $line, $m)) { $current_agents[] = strtolower(trim($m[1])); continue; } if (preg_match('/^disallow:\s*(.*)$/i', $line, $m)) { $rule = trim($m[1]); foreach ($current_agents as $ua) { $groups[$ua][] = $rule; } } } foreach (['googlebot', '*'] as $ua) { if (!empty($groups[$ua])) { foreach ($groups[$ua] as $rule) { if ($rule === '/') return true; } } } return false; } };