Build an SEO Checker Chrome Extension — Complete 2025 Tutorial

30 min read

Build an SEO Checker Chrome Extension — Complete 2025 Tutorial

Build an SEO Checker Chrome Extension — Complete 2025 Tutorial

In the competitive landscape of digital marketing and web development, having quick access to SEO insights can dramatically improve your workflow. Whether you’re a content creator auditing blog posts, a developer checking landing pages, or a marketer analyzing competitor sites, an SEO checker extension provides immediate on-page SEO analysis without leaving your browser. This comprehensive tutorial will walk you through building a fully functional SEO checker Chrome extension using modern Chrome extension development practices with Manifest V3.

The demand for efficient SEO tools has never been greater. Professionals across industries need quick ways to evaluate webpage optimization without navigating to external tools or manually inspecting source code. By building your own seo audit extension, you gain complete control over the features, customization, and privacy of your analysis tool. Unlike many online SEO tools that require subscriptions or limit daily checks, your custom extension runs locally in Chrome, providing unlimited analyses without any costs.

This tutorial covers everything from setting up your development environment to implementing comprehensive SEO checks, creating an intuitive user interface, and publishing your extension to the Chrome Web Store. By the end, you’ll have a professional-grade SEO checker that can analyze meta tags, headings, content readability, keyword density, image optimization, link structure, and Core Web Vitals metrics.


Prerequisites and Development Setup

Before diving into the implementation, ensure you have the necessary tools and knowledge. This project requires basic familiarity with HTML, CSS, and JavaScript, along with a Chrome browser for testing. You’ll also need Node.js installed for managing dependencies and building your extension.

Begin by creating a new project directory for your SEO checker extension. Inside this directory, you’ll organize your files according to Chrome’s extension structure. The essential files include the manifest.json configuration file, popup HTML and JavaScript for the user interface, content scripts for analyzing web pages, and background service workers for handling complex operations.

Chrome extensions using Manifest V3 require specific permission configurations. For an SEO checker extension, you’ll need the activeTab permission to access the current page’s content, storage permission for saving user preferences, and scripting permission for executing content scripts. Understanding these permissions is crucial for both functionality and Chrome Web Store approval.

Your manifest.json file serves as the configuration hub for the entire extension. Here’s a complete example tailored for an SEO checker:

{
  "manifest_version": 3,
  "name": "SEO Checker Pro",
  "version": "1.0.0",
  "description": "Comprehensive on-page SEO analysis and audit tool",
  "permissions": ["activeTab", "storage", "scripting"],
  "host_permissions": ["<all_urls>"],
  "action": {
    "default_popup": "popup.html",
    "default_icon": {
      "16": "icons/icon16.png",
      "48": "icons/icon48.png",
      "128": "icons/icon128.png"
    }
  },
  "content_scripts": [{
    "matches": ["<all_urls>"],
    "js": ["content.js"]
  }]
}

This configuration grants your extension the necessary permissions while following Manifest V3 best practices. The host_permissions field with “" allows your extension to analyze any webpage the user visits, which is essential for a universal SEO audit extension.


Core SEO Analysis Features

A comprehensive SEO checker extension must analyze multiple aspects of on-page optimization. This section details the essential checks your extension should implement, along with implementation strategies for each feature.

Meta Tag Analysis

Meta tags provide search engines with crucial information about your webpage content. Your extension should analyze title tags, meta descriptions, canonical URLs, and Open Graph tags. The title tag remains one of the most important on-page SEO factors, directly influencing search engine rankings and click-through rates in search results.

Implement meta tag analysis by querying the DOM for specific elements. Use document.querySelector to find the title tag and meta elements with name or property attributes. Here’s how to extract and analyze these critical elements:

function analyzeMetaTags() {
  const results = {
    title: {
      element: document.querySelector('title'),
      content: document.title,
      length: document.title.length,
      passed: false
    },
    metaDescription: {
      element: document.querySelector('meta[name="description"]'),
      content: '',
      length: 0,
      passed: false
    },
    canonical: {
      element: document.querySelector('link[rel="canonical"]'),
      href: '',
      passed: false
    },
    ogTags: {}
  };
  
  // Analyze meta description
  if (results.metaDescription.element) {
    results.metaDescription.content = 
      results.metaDescription.element.getAttribute('content') || '';
    results.metaDescription.length = results.metaDescription.content.length;
    results.metaDescription.passed = 
      results.metaDescription.length >= 120 && 
      results.metaDescription.length <= 160;
  }
  
  // Check canonical URL
  if (results.canonical.element) {
    results.canonical.href = 
      results.canonical.element.getAttribute('href') || '';
    results.canonical.passed = 
      results.canonical.href.length > 0 && 
      results.canonical.href.startsWith('http');
  }
  
  // Analyze Open Graph tags
  const ogTags = document.querySelectorAll('meta[property^="og:"]');
  ogTags.forEach(tag => {
    const property = tag.getAttribute('property');
    results.ogTags[property] = {
      content: tag.getAttribute('content'),
      passed: !!tag.getAttribute('content')
    };
  });
  
  // Title tag validation
  results.title.passed = 
    results.title.length >= 30 && 
    results.title.length <= 60;
  
  return results;
}

This function returns a structured analysis of all critical meta elements, including validation results for recommended lengths. The passed boolean indicates whether each element meets SEO best practices, making it easy to display clear pass/fail indicators in your extension’s UI.

Heading Structure and Content Hierarchy

Proper heading structure helps search engines understand your content’s organization. An effective on-page SEO chrome tool must analyze the heading hierarchy, checking for H1 tag usage, H2-H6 subheadings, and overall content structure. Every page should have exactly one H1 tag that accurately describes the page content.

Implement heading analysis by querying all heading elements and building a hierarchical map:

function analyzeHeadings() {
  const headings = {
    h1: {
      elements: document.querySelectorAll('h1'),
      count: 0,
      content: [],
      passed: false,
      issues: []
    },
    h2: {
      elements: document.querySelectorAll('h2'),
      count: 0,
      content: []
    },
    h3: {
      elements: document.querySelectorAll('h3'),
      count: 0,
      content: []
    },
    h4: { elements: document.querySelectorAll('h4'), count: 0 },
    h5: { elements: document.querySelectorAll('h5'), count: 0 },
    h6: { elements: document.querySelectorAll('h6'), count: 0 }
  };
  
  // Count and collect H1 content
  headings.h1.count = headings.h1.elements.length;
  headings.h1.elements.forEach(el => {
    headings.h1.content.push(el.textContent.trim());
  });
  
  // Validate H1 usage
  if (headings.h1.count === 0) {
    headings.h1.issues.push('Missing H1 tag - every page should have exactly one H1');
  } else if (headings.h1.count > 1) {
    headings.h1.issues.push(`Found ${headings.h1.count} H1 tags - should have exactly one`);
  } else {
    headings.h1.passed = true;
  }
  
  // Count remaining headings
  ['h2', 'h3', 'h4', 'h5', 'h6'].forEach(level => {
    headings[level].count = headings[level].elements.length;
    headings[level].content = Array.from(headings[level].elements)
      .map(el => el.textContent.trim());
  });
  
  return headings;
}

This analysis reveals common heading structure problems like missing H1 tags, multiple H1 tags, or empty headings. The passed flag helps users quickly identify whether their heading structure meets SEO best practices.

Content Readability and Keyword Analysis

Content readability significantly impacts both user engagement and SEO performance. Search engines favor content that’s easy to read and understand. Your extension should implement readability metrics like Flesch-Kincaid reading ease scores and analyze keyword usage patterns throughout the content.

Readability analysis examines sentence length, word complexity, and overall text structure. Implement a basic Flesch-Kincaid analyzer:

function analyzeReadability() {
  const bodyContent = document.body.innerText;
  const paragraphs = document.querySelectorAll('p');
  
  // Split into sentences and words
  const sentences = bodyContent.split(/[.!?]+/).filter(s => s.trim().length > 0);
  const words = bodyContent.split(/\s+/).filter(w => w.length > 0);
  
  // Calculate average sentence length
  const avgSentenceLength = sentences.length > 0 
    ? words.length / sentences.length 
    : 0;
  
  // Count syllables (basic estimation)
  const syllableCount = words.reduce((count, word) => {
    return count + countSyllables(word);
  }, 0);
  
  // Flesch-Kincaid Reading Ease
  const readingEase = sentences.length > 0 && words.length > 0
    ? 206.835 - 1.015 * (words.length / sentences.length) - 84.6 * (syllableCount / words.length)
    : 0;
  
  return {
    wordCount: words.length,
    sentenceCount: sentences.length,
    avgSentenceLength: Math.round(avgSentenceLength),
    readingEase: Math.round(readingEase),
    readabilityLevel: getReadabilityLevel(readingEase),
    paragraphCount: paragraphs.length,
    passed: readingEase >= 60
  };
}

function countSyllables(word) {
  word = word.toLowerCase().replace(/[^a-z]/g, '');
  if (word.length <= 3) return 1;
  
  word = word.replace(/(?:[^laeiouy]es|ed|[^laeiouy]e)$/, '');
  word = word.replace(/^y/, '');
  
  const syllables = word.match(/[aeiouy]{1,2}/g);
  return syllables ? syllables.length : 1;
}

function getReadabilityLevel(score) {
  if (score >= 90) return 'Very Easy';
  if (score >= 80) return 'Easy';
  if (score >= 70) return 'Fairly Easy';
  if (score >= 60) return 'Standard';
  if (score >= 50) return 'Fairly Difficult';
  if (score >= 30) return 'Difficult';
  return 'Very Difficult';
}

This readability analysis provides valuable insights into content quality. Users can quickly identify whether their content meets appropriate readability levels for their target audience.

Image Optimization Analysis

Images play a crucial role in both user experience and SEO. Your extension should analyze image alt attributes, file sizes, and lazy loading implementation. Images without proper alt text miss out on SEO opportunities and accessibility compliance.

Implement comprehensive image analysis:

function analyzeImages() {
  const images = document.querySelectorAll('img');
  const results = {
    total: images.length,
    withAlt: 0,
    withoutAlt: 0,
    altText: [],
    withTitle: 0,
    withoutTitle: 0,
    lazyLoaded: 0,
    missingDimensions: 0
  };
  
  images.forEach(img => {
    const alt = img.getAttribute('alt');
    const title = img.getAttribute('title');
    const loading = img.getAttribute('loading');
    const width = img.getAttribute('width');
    const height = img.getAttribute('height');
    
    if (alt !== null && alt.trim().length > 0) {
      results.withAlt++;
      results.altText.push(alt);
    } else {
      results.withoutAlt++;
    }
    
    if (title !== null) {
      results.withTitle++;
    } else {
      results.withoutTitle++;
    }
    
    if (loading === 'lazy') {
      results.lazyLoaded++;
    }
    
    if (!width || !height) {
      results.missingDimensions++;
    }
  });
  
  results.altTextScore = results.total > 0 
    ? Math.round((results.withAlt / results.total) * 100) 
    : 100;
  
  return results;
}

This analysis provides a comprehensive view of image optimization, highlighting areas needing improvement. The alt text score offers a quick metric for overall image SEO compliance.


Beyond content analysis, comprehensive SEO checking requires examining link structures and technical SEO factors. This section covers internal and external link analysis, mobile responsiveness checks, and Core Web Vitals assessment.

Links remain one of the most important ranking factors. Your extension should categorize links as internal (pointing to the same domain) or external (pointing to other domains), identify broken links, and analyze anchor text distribution:

function analyzeLinks() {
  const links = document.querySelectorAll('a[href]');
  const currentDomain = window.location.hostname;
  
  const results = {
    total: links.length,
    internal: 0,
    external: 0,
    internalDomains: new Set(),
    externalDomains: new Set(),
    anchorText: [],
    noFollow: 0,
    doFollow: 0,
    emptyAnchors: 0
  };
  
  links.forEach(link => {
    const href = link.getAttribute('href');
    const rel = link.getAttribute('rel');
    const text = link.textContent.trim();
    
    // Skip empty or javascript: links
    if (!href || href.startsWith('javascript:') || href.startsWith('#')) {
      return;
    }
    
    try {
      const url = new URL(href, window.location.origin);
      
      if (url.hostname === currentDomain || url.hostname.endsWith('.' + currentDomain)) {
        results.internal++;
        results.internalDomains.add(url.hostname);
      } else {
        results.external++;
        results.externalDomains.add(url.hostname);
      }
    } catch (e) {
      // Invalid URL, skip
    }
    
    // Analyze anchor text
    if (text.length > 0) {
      results.anchorText.push(text);
    } else {
      results.emptyAnchors++;
    }
    
    // Check nofollow
    if (rel && rel.includes('nofollow')) {
      results.noFollow++;
    } else {
      results.doFollow++;
    }
  });
  
  // Convert Sets to arrays for JSON serialization
  results.internalDomains = Array.from(results.internalDomains);
  results.externalDomains = Array.from(results.externalDomains);
  
  // Calculate internal/external ratio
  results.internalRatio = results.total > 0 
    ? Math.round((results.internal / results.total) * 100) 
    : 0;
  
  return results;
}

This link analysis provides insights into a page’s link profile, helping users understand their internal linking structure and external link relationships.

Core Web Vitals Assessment

Core Web Vitals have become essential ranking factors. Your extension can measure Largest Contentful Paint (LCP), First Input Delay (FID), and Cumulative Layout Shift (CLS) using the Chrome UI Diagnostics API when available, or through manual performance timing:

function analyzeCoreWebVitals() {
  const results = {
    LCP: { value: 0, rating: 'unknown' },
    FID: { value: 0, rating: 'unknown' },
    CLS: { value: 0, rating: 'unknown' },
    FCP: { value: 0, rating: 'unknown' },
    loadTime: { value: 0, rating: 'unknown' }
  };
  
  // Timing-based metrics
  const timing = performance.timing;
  const navigation = performance.getEntriesByType('navigation')[0];
  
  if (navigation) {
    results.loadTime.value = Math.round(navigation.loadEventEnd - navigation.fetchStart);
    
    // Calculate LCP approximation
    const lcpEntry = performance.getEntriesByType('largest-contentful-paint')[0];
    if (lcpEntry) {
      results.LCP.value = Math.round(lcpEntry.startTime);
    }
    
    // Calculate CLS
    const clsValue = performance.getEntriesByType('layout-shift')
      .filter(entry => !entry.hadRecentInput)
      .reduce((sum, entry) => sum + entry.value, 0);
    results.CLS.value = Math.round(clsValue * 100) / 100;
  }
  
  // Rate the metrics
  results.LCP.rating = rateLCP(results.LCP.value);
  results.CLS.rating = rateCLS(results.CLS.value);
  results.loadTime.rating = rateLoadTime(results.loadTime.value);
  
  return results;
}

function rateLCP(value) {
  if (value === 0) return 'unknown';
  if (value <= 2500) return 'good';
  if (value <= 4000) return 'needs improvement';
  return 'poor';
}

function rateCLS(value) {
  if (value <= 0.1) return 'good';
  if (value <= 0.25) return 'needs improvement';
  return 'poor';
}

function rateLoadTime(value) {
  if (value <= 3000) return 'good';
  if (value <= 6000) return 'needs improvement';
  return 'poor';
}

These Core Web Vitals metrics provide users with performance insights directly in their browser, helping them identify and address page speed issues.


Building the User Interface

The popup interface serves as the primary interaction point for your SEO checker extension. It should display analysis results clearly, providing users with actionable insights. Design a clean, intuitive interface that presents complex data in an easily digestible format.

Create a popup.html file with a well-structured layout:

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>SEO Checker Pro</title>
  <style>
    * { box-sizing: border-box; margin: 0; padding: 0; }
    body {
      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
      width: 400px;
      padding: 16px;
      background: #f5f5f5;
    }
    .header {
      display: flex;
      align-items: center;
      gap: 12px;
      margin-bottom: 16px;
      padding-bottom: 12px;
      border-bottom: 1px solid #e0e0e0;
    }
    .header h1 {
      font-size: 18px;
      color: #333;
    }
    .score-card {
      background: white;
      border-radius: 8px;
      padding: 16px;
      margin-bottom: 12px;
      box-shadow: 0 1px 3px rgba(0,0,0,0.1);
    }
    .score-display {
      text-align: center;
      padding: 20px;
    }
    .score-value {
      font-size: 48px;
      font-weight: bold;
      color: #34a853;
    }
    .score-label {
      font-size: 14px;
      color: #666;
      margin-top: 4px;
    }
    .section {
      background: white;
      border-radius: 8px;
      padding: 12px;
      margin-bottom: 8px;
      box-shadow: 0 1px 3px rgba(0,0,0,0.1);
    }
    .section-title {
      font-size: 14px;
      font-weight: 600;
      color: #333;
      margin-bottom: 8px;
      display: flex;
      justify-content: space-between;
      align-items: center;
    }
    .check-item {
      display: flex;
      justify-content: space-between;
      align-items: center;
      padding: 8px 0;
      border-bottom: 1px solid #f0f0f0;
    }
    .check-item:last-child { border-bottom: none; }
    .check-label { font-size: 13px; color: #555; }
    .check-status {
      font-size: 12px;
      padding: 2px 8px;
      border-radius: 12px;
      font-weight: 500;
    }
    .status-pass { background: #e6f4ea; color: #34a853; }
    .status-fail { background: #fce8e6; color: #ea4335; }
    .status-warn { background: #fef7e0; color: #fbbc04; }
    .btn-analyze {
      width: 100%;
      padding: 12px;
      background: #4285f4;
      color: white;
      border: none;
      border-radius: 6px;
      font-size: 14px;
      font-weight: 500;
      cursor: pointer;
      margin-bottom: 12px;
    }
    .btn-analyze:hover { background: #3367d6; }
    .btn-analyze:disabled { background: #ccc; cursor: not-allowed; }
  </style>
</head>
<body>
  <div class="header">
    <h1>🔍 SEO Checker Pro</h1>
  </div>
  
  <button id="analyzeBtn" class="btn-analyze">Analyze This Page</button>
  
  <div id="results" style="display: none;">
    <div class="score-card">
      <div class="score-display">
        <div id="overallScore" class="score-value">0</div>
        <div class="score-label">Overall SEO Score</div>
      </div>
    </div>
    
    <div class="section">
      <div class="section-title">Meta Tags</div>
      <div id="metaResults"></div>
    </div>
    
    <div class="section">
      <div class="section-title">Content</div>
      <div id="contentResults"></div>
    </div>
    
    <div class="section">
      <div class="section-title">Images</div>
      <div id="imageResults"></div>
    </div>
    
    <div class="section">
      <div class="section-title">Links</div>
      <div id="linkResults"></div>
    </div>
  </div>
  
  <script src="popup.js"></script>
</body>
</html>

This interface provides a clean, organized display of SEO analysis results. The design uses clear visual indicators for passing and failing checks, making it easy for users to quickly identify areas needing attention.


Connecting the Popup with Content Scripts

The popup communicates with content scripts to retrieve SEO analysis data from the active webpage. Implement the messaging system in your popup.js:

document.getElementById('analyzeBtn').addEventListener('click', async () => {
  const btn = document.getElementById('analyzeBtn');
  btn.textContent = 'Analyzing...';
  btn.disabled = true;
  
  try {
    // Get the active tab
    const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
    
    // Execute content script to analyze the page
    const results = await chrome.scripting.executeScript({
      target: { tabId: tab.id },
      function: runFullAnalysis
    });
    
    // Display results
    displayResults(results[0].result);
    
  } catch (error) {
    console.error('Analysis failed:', error);
    alert('Failed to analyze page. Please refresh and try again.');
  }
  
  btn.textContent = 'Analyze This Page';
  btn.disabled = false;
});

// This function runs in the context of the webpage
function runFullAnalysis() {
  return {
    meta: analyzeMetaTags(),
    headings: analyzeHeadings(),
    readability: analyzeReadability(),
    images: analyzeImages(),
    links: analyzeLinks(),
    webVitals: analyzeCoreWebVitals()
  };
}

function displayResults(results) {
  document.getElementById('results').style.display = 'block';
  
  // Calculate overall score
  const score = calculateOverallScore(results);
  const scoreEl = document.getElementById('overallScore');
  scoreEl.textContent = score;
  scoreEl.style.color = getScoreColor(score);
  
  // Display meta results
  displayMetaResults(results.meta);
  displayContentResults(results);
  displayImageResults(results.images);
  displayLinkResults(results.links);
}

function calculateOverallScore(results) {
  let total = 0;
  let passed = 0;
  
  // Meta tags (30 points)
  total += 30;
  if (results.meta.title.passed) passed += 10;
  if (results.meta.metaDescription.passed) passed += 10;
  if (results.meta.canonical.passed) passed += 10;
  
  // Headings (20 points)
  total += 20;
  if (results.headings.h1.passed) passed += 20;
  
  // Content (20 points)
  total += 20;
  if (results.readability.passed) passed += 10;
  if (results.readability.wordCount >= 300) passed += 10;
  
  // Images (15 points)
  total += 15;
  if (results.images.altTextScore >= 90) passed += 15;
  
  // Links (15 points)
  total += 15;
  if (results.links.internal > 0) passed += 10;
  if (results.links.external > 0) passed += 5;
  
  return Math.round((passed / total) * 100);
}

function getScoreColor(score) {
  if (score >= 80) return '#34a853';
  if (score >= 60) return '#fbbc04';
  return '#ea4335';
}

This JavaScript handles the communication between the popup and the content script, calculates an overall SEO score, and updates the UI with results. The modular design makes it easy to add additional analysis features.


Testing and Publishing Your Extension

Before publishing to the Chrome Web Store, thoroughly test your extension across different websites and page types. Create test cases covering various scenarios: well-optimized pages, poorly optimized pages, single-page applications, and pages with dynamic content.

Load your extension in Chrome for testing by navigating to chrome://extensions, enabling Developer mode, and clicking “Load unpacked”. Select your extension’s directory to install it temporarily. Test the extension on multiple websites, checking that all analysis features work correctly and the UI displays properly.

For Chrome Web Store publication, prepare your store listing with compelling screenshots, a detailed description, and appropriate categories. Ensure your extension follows all Chrome Web Store policies, particularly regarding privacy and data usage. Create a developer account if you don’t already have one, and submit your extension for review.

Your SEO checker extension is now complete and ready for distribution. Users can install it from the Chrome Web Store to perform instant on-page SEO audits on any webpage they visit, making it an invaluable tool for marketers, developers, and content creators alike.


Conclusion

Building an SEO checker Chrome extension is an excellent project that combines practical utility with powerful browser API usage. You’ve learned how to analyze meta tags, headings, content readability, images, links, and Core Web Vitals—all essential components of comprehensive on-page SEO analysis.

The extension you built follows Manifest V3 best practices, ensuring compatibility with modern Chrome extension standards. The clean, intuitive interface makes SEO analysis accessible to users of all skill levels, while the comprehensive feature set provides professional-grade auditing capabilities.

Consider expanding your extension with additional features like competitive analysis, keyword tracking, or integration with SEO APIs. The foundation you’ve built provides an excellent starting point for creating even more powerful SEO tools. With the growing importance of search engine optimization in digital marketing, a well-designed seo audit extension can serve thousands of users seeking to improve their website’s visibility and performance.

No previous article
No next article