How Audit Issue Reporters Identify Common SEO Problems in Open SEO
Audit issue reporters in Open SEO identify common SEO problems through a layered pipeline that checks per-page signals like missing titles and slow responses, then runs cross-page algorithms to detect duplicates and redirect loops.
Open SEO uses a systematic audit pipeline to transform raw crawl data into actionable SEO insights. The audit issue reporters work by comparing crawled page attributes against a centralized catalogue of SEO rules, enabling automated detection of technical problems ranging from missing meta tags to site-wide duplicate content.
The Centralized Issue Catalogue
All possible SEO problems are defined once in the shared module src/shared/audit-issues.ts. This central registry ensures consistent wording and severity levels across both the server-side engine and the client UI.
Each entry in the AUDIT_ISSUE_TYPES constant contains a severity level, human-readable title, detailed explanation, and concrete remediation steps:
export const AUDIT_ISSUE_TYPES = {
"missing-title": { severity: "critical", title: "Missing title tag", … },
"duplicate-meta-description": { … },
// … (≈ 30 issue types)
} as const;
The catalogue contains approximately 30 distinct issue types, providing a single source of truth for the entire audit system.
Per-Page SEO Detection with runPageReporters
For every crawled page, the pure function runPageReporters in src/server/lib/audit/issues/page-reporters.ts executes a series of DOM-free checks. The function accepts a CrawledPageResult object and returns an array of DetectedIssue objects referencing the central catalogue.
The reporter evaluates the following SEO signal categories:
- Fetch status – Detects
blocked-pagewhen bot protection triggers,server-errorfor HTTP 5xx responses, andbroken-pagefor HTTP 4xx errors (lines 49‑64). - Performance – Flags
slow-responsewhen response time exceeds 1.5 seconds (lines 70‑72). - Title tags – Identifies
missing-title,title-too-long, andtitle-too-shortbased on length thresholds (lines 80‑87). - Meta descriptions – Reports
missing-meta-description,meta-description-too-long, andmeta-description-too-short(lines 90‑99). - Heading structure – Catches
missing-h1,multiple-h1, andheading-order-skipwhen hierarchy is violated (lines 101‑110). - Canonical and indexability – Detects
noindex-page,canonical-conflict, andcanonicalized-pageissues (lines 112‑133). - Content quality – Flags
thin-contentfor pages under 150 words andimages-missing-altfor accessibility gaps (lines 134‑144). - Link architecture – Identifies
no-outgoing-linksanddeep-pagefor URLs buried deeper than five clicks (lines 46‑52).
Each condition pushes a DetectedIssue into the result array using a simple pattern:
if (!page.title) report("missing-title");
Practical Example: Running Page-Level Checks
You can invoke the per-page reporter directly for unit testing or custom integrations:
import { runPageReporters } from "@/server/lib/audit/issues/page-reporters";
import type { CrawledPageResult } from "@/server/lib/audit/types";
const page: CrawledPageResult = {
id: "p1",
url: "https://example.com/about",
fetchClass: "ok",
statusCode: 200,
responseTimeMs: 2100,
isHtml: true,
title: "",
metaDescription: "Short",
h1Count: 0,
headingOrder: [1, 3],
isIndexable: true,
canonicalUrl: null,
headerCanonicalUrl: null,
wordCount: 90,
imagesMissingAlt: 2,
imagesTotal: 10,
links: [],
crawlDepth: 6,
};
const issues = runPageReporters(page);
console.log(issues.map(i => i.issueType));
/* → [
"slow-response",
"missing-title",
"title-too-short",
"missing-h1",
"heading-order-skip",
"thin-content",
"images-missing-alt",
"no-outgoing-links",
"deep-page"
] */
This implementation in src/server/lib/audit/issues/page-reporters.ts processes each page independently, ensuring fast, stateless evaluation.
Cross-Page (Multipage) Analysis
Some SEO problems require visibility into the entire site structure. The src/server/lib/audit/issues/multipage-checks.ts module operates on a lightweight SlimPage representation to detect site-wide patterns without loading full page content into memory.
Duplicate Content Detection
The findDuplicates function groups pages by title, meta description, and content hash. Any group with two or more members generates duplicate-title, duplicate-meta-description, or duplicate-content issues for all affected URLs.
import { findDuplicates } from "@/server/lib/audit/issues/multipage-checks";
const pages = [
{ id: "1", url: "/a", statusCode: 200, fetchClass: "ok", title: "Home", metaDescription: "Home page", contentHash: "hash1", wordCount: 300, isIndexable: true, canonicalUrl: null, headerCanonicalUrl: null },
{ id: "2", url: "/b", statusCode: 200, fetchClass: "ok", title: "Home", metaDescription: "About us", contentHash: "hash2", wordCount: 250, isIndexable: true, canonicalUrl: null, headerCanonicalUrl: null },
];
const dupIssues = findDuplicates(pages);
console.log(dupIssues);
/* → [{ issueType: "duplicate-title", pageId: "1", … }, { issueType: "duplicate-title", pageId: "2", … }] */
Redirect Chain and Loop Detection
The system walks the graph of 3xx redirects to identify structural problems. Long chains emit redirect-chain issues, while cycles trigger redirect-loop alerts. These checks run after the per-page reporters complete, ensuring all crawl data is available for graph analysis.
Orchestration Through the MCP API
The audit pipeline is exposed through Model Context Protocol (MCP) tools defined in src/server/mcp/tools/site-audit-tools.ts. The run_site_audit tool initiates the process:
- Crawls the target site and stores each
CrawledPageResult. - Calls
runPageReportersfor every crawled page to detect single-page issues. - Persists pages to the database and executes multipage checks (
findDuplicates,findRedirectChainsAndLoops). - Stores results in the
audit_issuestable.
The get_audit_issues endpoint retrieves the final list, sorting by severity and decorating each row with human-friendly descriptions from AUDIT_ISSUE_TYPES.
Starting an Audit via API
// Example: start an audit for https://example.com
await fetch("https://api.openseo.com/mcp/run_site_audit", {
method: "POST",
headers: { "Content-Type": "application/json", Authorization: "Bearer <token>" },
body: JSON.stringify({
projectId: "proj_123",
url: "https://example.com",
maxPages: 200,
runLighthouse: true,
}),
});
// The response contains an auditId; poll get_audit_status until completed.
The workflow driver in src/server/workflows/siteAuditWorkflowPhases.ts manages execution phases, ensuring reporters run only after crawl completion.
Summary
- Audit issue reporters rely on a centralized catalogue in
src/shared/audit-issues.tsto maintain consistent issue definitions across the platform. - Per-page detection occurs through the pure function
runPageReporters, which evaluates fetch status, performance, titles, meta descriptions, headings, canonicals, content quality, and link structure without DOM parsing. - Cross-page analysis uses
findDuplicatesandfindRedirectChainsAndLoopsto detect site-wide problems like duplicate content and redirect loops. - MCP orchestration via
src/server/mcp/tools/site-audit-tools.tscoordinates crawling, analysis, and result persistence through standardized API endpoints.
Frequently Asked Questions
How does Open SEO determine the severity of an SEO issue?
Severity levels are hardcoded in the AUDIT_ISSUE_TYPES registry within src/shared/audit-issues.ts. Each issue type specifies a severity of "critical", "warning", or "info" based on the potential impact on search rankings. For example, missing-title carries critical severity because it directly impacts click-through rates from search results, while title-too-long might warrant a warning.
Can I run the audit issue reporters on a single page without crawling an entire site?
Yes. The runPageReporters function in src/server/lib/audit/issues/page-reporters.ts is a pure function that accepts a single CrawledPageResult object. You can construct this object manually or from any data source and call the function directly without invoking the full MCP crawl workflow, making it suitable for unit tests or custom integrations.
What is the difference between page-level and multipage SEO checks?
Page-level checks evaluate attributes of individual URLs in isolation, such as title length or response time, using runPageReporters. Multipage checks require analyzing relationships between multiple URLs, such as detecting duplicate titles across different pages or identifying redirect chains that span several URLs. These are handled by findDuplicates and findRedirectChainsAndLoops in src/server/lib/audit/issues/multipage-checks.ts.
How are detected audit issues stored and retrieved?
After detection, issues are persisted to the audit_issues database table. The MCP endpoint get_audit_issues queries this table, sorts results by severity, and enriches each record with human-readable titles and remediation steps from the AUDIT_ISSUE_TYPES catalogue. This architecture separates detection logic from storage, allowing reporters to remain stateless while the database maintains the audit history.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →