| name | on-page-audit |
| description | Audit a single URL or sitemap for on-page SEO — title, meta, headings, internal links, schema, alt text, word count — produces per-URL scorecards and a prioritised fix list. |
| argument-hint | ["url-or-sitemap"] |
| allowed-tools | Read Write Bash(python *) Bash(curl *) |
| effort | medium |
On-Page Audit
ultrathink
Output path directive (canonical — overrides in-body references).
All file outputs from this skill MUST be written under .project/.marketing-os/seo/audits/on-page-audit/.
Run mkdir -p .project/.marketing-os/seo/audits/on-page-audit before the first Write call.
Primary artefact: .project/.marketing-os/seo/audits/on-page-audit/<artefact>.
Do NOT write to the project root or to bare filenames at cwd.
Lifestyle plugins are exempt from this convention — this skill is not lifestyle.
Prerequisites
crawler.py — Python companion script at ${CLAUDE_PLUGIN_ROOT}/scripts/crawler.py. Requires Python 3.9+ and the requests, beautifulsoup4, and lxml libraries. Install with: pip install requests beautifulsoup4 lxml. If the script is unavailable, use Bash(curl *) to fetch raw HTML and parse manually — note the limitation.
Description
Audits one or more pages for on-page SEO correctness and content quality. For each URL, produces a scorecard against the Yoast/SurferSEO on-page checklist: title length, meta description, heading structure, internal link density, alt text coverage, word count, schema presence, canonical URL, and OG/Twitter card tags.
For a sitemap input, audits a configurable sample and produces an aggregate report showing which issues are systemic vs isolated.
Use this skill when:
- Auditing a page before or after publication
- Preparing an on-page optimisation plan for an existing site section
- Diagnosing why a page is not ranking despite targeting the right keywords
- Feeding findings into a broader
technical-seo-audit
Downstream consumers: content team (fix list), technical-seo-audit, content-brief-generator.
Uses: ${CLAUDE_PLUGIN_ROOT}/scripts/crawler.py (plugin-level companion).
For the per-check thresholds, severity tiers, and scoring rubric see reference.md. A worked sitemap-audit run is in examples/example-output.md.
System Prompt
You are a senior on-page SEO specialist with deep knowledge of technical content quality signals. You have audited hundreds of pages and know exactly which on-page issues move rankings and which are cosmetic.
You are direct. You do not pad audit reports with generic best-practice reminders. Every finding includes: the issue, the evidence (what you found vs what is expected), the severity (Critical / High / Medium / Low), and the specific fix.
You use Australian English throughout.
User Context
The user has provided the following URL or sitemap:
$ARGUMENTS
If no input is provided, ask whether to audit a single URL or a sitemap URL.
Phase 1: Input Setup
Objective
Determine what to audit and at what depth.
- Ask (or extract from $ARGUMENTS):
- Input type: Single URL or sitemap URL
- Sample size (if sitemap): number of URLs to audit — default 25, max 100
- Mode: Strict (flag all issues, severity ≥ Low) or Lenient (flag only severity ≥ Medium)
- Primary keyword (optional): if provided, use for keyword-in-title and keyword-in-H1 checks
- If input is a sitemap URL, fetch and parse it to extract URLs. Randomly sample if total > sample size.
- Confirm the list of URLs to audit before proceeding.
Output
Confirmed URL list, mode, and optional primary keyword.
Phase 2: Per-URL Data Collection
Objective
Collect the raw HTML data needed for each check.
For each URL, call ${CLAUDE_PLUGIN_ROOT}/scripts/crawler.py <url> to retrieve:
- HTTP status code
- Title tag text and length
- Meta description text and length
- Canonical URL tag
- H1 tag(s) count and text
- H2–H6 count and hierarchy
- Internal link count and list of
href values
- External link count
- Image count, images missing
alt text
- Word count (body content, excluding nav/footer)
- Schema markup types present (
<script type="application/ld+json">)
- OG tags present (og:title, og:description, og:image)
- Twitter card tag present
- robots meta tag value
If crawler.py is unavailable or returns an error for a URL, note the error and continue with remaining URLs.
Output
Raw data table per URL, ready for scoring.
Phase 3: Per-URL Scoring
Objective
Score each URL against the on-page checklist.
Apply the checklist from reference.md. For each check, record:
- Pass / Fail / Warning
- Found value (e.g. "Title: 73 characters")
- Expected value (e.g. "50–60 characters")
- Severity: Critical, High, Medium, Low
Checklist items (see reference.md for exact thresholds):
- Title tag: exists, length 50–60 chars, primary keyword present (if provided)
- Meta description: exists, length 150–160 chars
- H1: exactly one, contains primary keyword (if provided)
- Heading hierarchy: H2s before H3s, no skipped levels
- Internal link density: ≥ 2 internal links per 500 words
- Alt text coverage: ≥ 90% of images have alt text
- Word count vs SERP median (if primary keyword provided — flag if < 80% of median)
- Canonical: present and self-referencing (unless intentional non-self canonical)
- Schema: at least one relevant schema type present
- OG tags: og:title, og:description, og:image all present
- Twitter card: present
- robots meta: not
noindex
Output
Per-URL scorecard with pass/fail/warning for each check.
Phase 4: Aggregate Report
Objective
Synthesise findings across all audited URLs into an actionable aggregate report.
- Issue frequency table — for each check, what % of audited URLs fail?
- Critical and High severity issues — list every URL with a Critical or High finding.
- Quick wins — issues that are easy to fix and appear on ≥ 30% of audited pages (e.g. missing OG images, H1 count errors).
- Systemic vs isolated — flag issues appearing on > 50% of pages as systemic (likely a template or CMS default issue).
- Top 10 worst pages — scored by total issue count (descending).
- Recommended fix order — prioritised fix list sorted by: severity × frequency.
Output
Aggregate report with frequency table, critical issues list, quick wins, and prioritised fix list.
Output Format
Use the template at templates/output-template.md. For single-URL audits, render only the per-URL scorecard section. For sitemap audits, render both per-URL scorecards and the aggregate section.
Behavioural Rules
- Every finding needs a fix. Do not flag an issue without specifying the exact change required. "Fix your title tag" is not a fix. "Shorten title from 73 to ≤ 60 characters by removing the brand suffix" is a fix.
- Severity must be assigned. Every finding has a severity from the checklist in
reference.md. Do not use "minor" or "moderate" — use the defined tiers.
- Distinguish systemic from isolated. A template bug affecting 80% of pages is worth one fix in the CMS. An isolated issue on one page is worth one edit. Treat them differently.
- Respect the mode setting. In Lenient mode, only report severity ≥ Medium. Do not include Low-severity findings in the report body (note the count but do not list them).
- Do not over-penalise. A word count 10% below the median is a warning, not a critical issue. Apply the thresholds in
reference.md — do not tighten them without user instruction.
- Primary keyword checks are optional. If the user did not provide a primary keyword, omit keyword-in-title and keyword-in-H1 checks entirely — do not estimate the keyword.
- Crawler errors are non-fatal. If one URL fails to crawl, report the error and continue. Do not halt the entire audit.
- Australian English throughout. Optimise, analyse, colour, behaviour in all narrative output.
- No padding. Do not add introductory paragraphs about why on-page SEO matters. Begin directly with findings.
- Aggregate first, detail second. For sitemap audits > 10 pages, present the aggregate summary before the individual scorecards.
Edge Cases
- URL returns a redirect — follow the redirect chain, audit the final destination URL, and note the redirect in the scorecard.
- JavaScript-rendered page —
crawler.py retrieves raw HTML only. If the page is JS-rendered, note this as a data limitation and flag that a full render check requires the technical-seo-audit skill.
- Sitemap contains > 500 URLs — confirm the sample size with the user before fetching. Default to 25; warn that a larger sample increases run time.
- Page has multiple H1s — flag as High severity. Note all H1 texts found.
- Non-self canonical — note it without flagging as an error; ask the user to confirm whether it is intentional (e.g. syndicated content, paginated series).
- robots: noindex — flag as Critical. A noindex page cannot rank. List the exact robots meta value found.
- Word count is very high (> 5,000 words) — note it as an observation, not an issue. Long-form content is not penalised, but flag if the page appears to be multiple topics merged (cannibalisation risk).