feat: add seo-audit skill (#19)

* feat: add seo-audit skill

Signed-off-by: Magnus Hedemark <magnus919@pm.me>

* fix: remove host-specific port assumptions

Signed-off-by: Magnus Hedemark <magnus919@pm.me>

* fix: generalize seo examples

Signed-off-by: Magnus Hedemark <magnus919@pm.me>

* fix: anonymize site-specific seo examples

Signed-off-by: Magnus Hedemark <magnus919@pm.me>

* fix: generalize portable examples

Signed-off-by: Magnus Hedemark <magnus919@pm.me>

---------

Signed-off-by: Magnus Hedemark <magnus919@pm.me>
This commit is contained in:
Magnus Hedemark
2026-07-12 14:46:41 -04:00
committed by GitHub
parent fd0b236745
commit 1624ef76f9
12 changed files with 1210 additions and 0 deletions
+1
View File
@@ -138,6 +138,7 @@ When the user mentions these keywords, load the corresponding skill:
| "c4-diagramming", "c4 diagramming" | [c4-diagramming](c4-diagramming/SKILL.md) |
| "technology-radar", "technology radar" | [technology-radar](technology-radar/SKILL.md) |
| "verification-methodology", "verification methodology" | [verification-methodology](verification-methodology/SKILL.md) |
| "seo-audit", "seo audit" | [seo-audit](seo-audit/SKILL.md) |
## Use-When Sections
Every skill description must identify when to load it. Skills with meaningful overlap should also include a `## When not to use` section naming the nearest alternative or prerequisite. Keep these sections trigger-oriented and concise; implementation details belong in references.
+4
View File
@@ -207,6 +207,10 @@ Turn an open question into a bounded, evidence-led investigation rather than a p
Give authorized teams a disciplined way to identify and prioritize security risks without mistaking a checklist for a security guarantee.
### [seo-audit](seo-audit/SKILL.md)
Identify search-discoverability problems through evidence, prioritize the work, and distinguish technical defects from content opportunities.
### [site-reliability-engineering](site-reliability-engineering/SKILL.md)
Build practical reliability practices around the work teams actually perform: measurable service objectives, useful alerts, incident response, and learning-oriented follow-up.
+37
View File
@@ -0,0 +1,37 @@
# Seo Audit
Identify search-discoverability problems through evidence, prioritize the work, and distinguish technical defects from content opportunities.
## Why Install This Skill
Identify search-discoverability problems through evidence, prioritize the work, and distinguish technical defects from content opportunities. It preserves a practical method, local reference material, and reusable templates so an agent can do more than produce a generic answer.
Use it when the work needs a repeatable process and an inspectable result. It is portable across Agent Skills-compatible clients and does not require a profile system or a particular task orchestrator.
## What You Get
| Path | What it provides |
|---|---|
| `SKILL.md` | Trigger conditions, workflow, and guidance for loading deeper resources. |
| `references/` | Reference material: `aeo-methodology.md`, `content-strategy-seo.md`, `ghost-metadata.md`, `onpage-seo.md`, `schema-markup.md`, `technical-seo.md` |
| `assets/` | Assets: `audit-report-template.md` |
## Quick Start
Start with the technical, on-page, or schema reference that matches the page or site under review.
Install or expose this directory using your agent's standard Agent Skills loading mechanism, then ask for work that matches the triggers below.
## Triggers
- Audit websites and pages for technical SEO, on-page SEO, schema markup, content discoverability, and answer-engine readiness. Use when prioritizing search visibility improvements.
- Requests involving the method, deliverables, or review process described in `SKILL.md`.
- Work where a reusable template or reference from this skill would reduce avoidable mistakes.
## Requirements
Requires access to the site or page being audited. Platform-specific references are optional and must be applied only when relevant.
## Source and maintenance
This skill was extracted from [`magnus919/hermes-profiles`](https://github.com/magnus919/hermes-profiles) at commit [`867a555`](https://github.com/magnus919/hermes-profiles/commit/867a555). The portable methodology was retained; Hermes-specific profile, orchestration, and memory assumptions were removed.
+66
View File
@@ -0,0 +1,66 @@
---
name: seo-audit
description: Audit websites and pages for technical SEO, on-page SEO, schema markup, content discoverability, and answer-engine readiness. Use when prioritizing search visibility improvements.
license: MIT
compatibility: Requires access to the site or page being audited. Platform-specific references are optional and must be applied only when relevant.
metadata:
source_repo: https://github.com/magnus919/hermes-profiles
source_commit: 867a555
---
# SEO Audit
Full-spectrum audit for sites, articles, and content strategies. Covers both traditional SEO and Answer Engine Optimization (AEO/GEO) — the practice of structuring content so LLMs and AI answer engines (ChatGPT, Perplexity, Gemini, Claude, Google AI Overviews) extract and cite it.
Use `assets/audit-report-template.md` for a portable audit deliverable. If the host workflow uses artifact pyramids, that structure can be an optional presentation format rather than a prerequisite.
## SEO + AEO Audit: [Site/Page URL]
**Overall Health:** [Good / Fair / Poor]
**Score:** [N/100]
### SEO Priority Findings
1. [Critical] → [Action]
### AEO Priority Findings
1. [Critical] → [Action]
### Quick Wins
1. [Low effort, high impact] → [Action]
### Verdict
[One paragraph summarizing the single most important thing to fix and expected impact.]
```
## Contents
| File | What it covers |
|------|----------------|
| `references/technical-seo.md` | Crawlability, indexability, robots.txt, sitemaps, page speed, Core Web Vitals, mobile-friendliness, HTTPS, canonical URLs, hreflang |
| `references/onpage-seo.md` | Title tags, meta descriptions, heading hierarchy, keyword placement, content quality, internal linking, image optimization |
| `references/schema-markup.md` | Schema.org types (TechArticle, FAQPage, HowTo, Article, BlogPosting, BreadcrumbList, Organization), JSON-LD format, Google rich results, validation |
| `references/content-strategy-seo.md` | Topic clusters, pillar pages, keyword research, gap analysis, SERP feature targeting, topical authority |
| `references/ghost-metadata.md` | Ghost CMS metadata fields (meta_title, meta_description, custom_excerpt), social media cards (OG, Twitter), code-injected schema (JSON-LD, FAQPage, TechArticle, @graph), per-post and site-wide injection, validation |
| `references/aeo-methodology.md` | Answer Engine Optimization — LLM RAG pipeline, answer-first content architecture, AEO-specific schema (FAQPage 3.2× boost), Ethan Smith/Graphite frameworks, question clusters, llms.txt, content negotiation, measurement |
| `assets/audit-report-template.md` | Blank report scaffold for new audits |
## When to Use
Load this skill when:
- Auditing a site for technical SEO issues
- Optimizing a new article for search and AI citation before publication
- Validating structured data on an existing page
- Completing Ghost CMS metadata (meta, social cards, schema injection) before publish
- Running an AEO readiness audit (llms.txt, content negotiation, question-cluster coverage)
- Developing a content strategy with SEO + AEO in parallel
- Diagnosing why a site or page isn't performing in search or AI citation
Do NOT load when:
- Only mechanical content fixes are needed; use the host agent's copy-editing workflow.
- Only writing is needed; use the host agent's writing workflow.
## Portability
This skill is intentionally host-neutral. Use your agent's normal mechanisms to load the references, templates, and scripts listed here. Do not assume a particular profile system, task orchestrator, memory service, or response-handoff format.
+76
View File
@@ -0,0 +1,76 @@
# SEO Audit Report Template
Scaffold for new SEO audit projects. Copy this into your pyramid root.
```markdown
# SEO Audit: [Site/Page URL]
**Audit date:** YYYY-MM-DD
**Auditor:** seo-specialist profile
**Scope:** [Full site / Single page / Content strategy]
---
## L1 Summary
### Overall Health: [Good / Fair / Poor]
### Score: [N/100]
### Priority Findings
1. [Critical] → [Action]
2. [Critical] → [Action]
### Moderate Findings
1. [Important] → [Action]
### Quick Wins
1. [Low effort, high impact] → [Action]
---
## L2 Analysis Files
### Technical SEO
- Crawlability and indexability
- Page speed / Core Web Vitals
- Mobile-friendliness
- HTTPS and security
- Canonical URLs and hreflang
- Sitemap and robots.txt
### On-Page SEO
- Title tags and meta descriptions
- Heading structure
- Keyword usage
- Content quality and length
- Internal linking
- Image optimization
- URL structure
### Schema Validation
- JSON-LD structure
- Schema.org types used
- Rich result eligibility
- Validation results
### Content Gap Analysis
- Target keywords
- Current rankings
- Competitor analysis
- Gap identification
- Priority recommendations
---
## L3 Dossiers
### Raw Data
- Full crawl results
- Schema validation output
- Keyword research data
- Competitor SERP data
### Methodology Notes
- Tools used
- Assumptions and limitations
- Data collection dates
+177
View File
@@ -0,0 +1,177 @@
# Answer Engine Optimization (AEO) Methodology
AEO (also called GEO — Generative Engine Optimization) is the practice of structuring content so AI-powered answer engines (ChatGPT, Perplexity, Gemini, Claude, Google AI Overviews) extract, cite, and surface it in their generated responses.
**Core insight:** ChatGPT traffic converts 6× better than Google search (Ethan Smith, Graphite). Content that ranks in Google is 3× more likely to appear in LLM citations. SEO and AEO are complementary — the SEO foundation feeds AEO performance.
## How LLMs Surface Content
Modern LLMs with web access use Retrieval-Augmented Generation (RAG):
1. **Query expansion** — User prompt is expanded into related sub-queries
2. **Retrieval** — Web search or vector DB finds candidate pages
3. **Re-ranking** — Chunks scored by relevance to query
4. **Synthesis** — LLM generates answer grounded in top chunks
5. **Citation** — Sources appended as footnotes or inline links
### Key Signals for Citation Selection
**Semantic relevance** — content must match semantic meaning, not just keywords. Named entities (people, products, brands, places) boost relevance scoring.
**Answer placement** — First 1-2 sentences of a section are the most extractable. Pages with clean heading hierarchy earn 2.8× higher citation rates (AirOps 2026).
**Authority** — 96% of AI Overview citations come from sources Google already trusts (Ziptie.dev). E-E-A-T functions as a binary gatekeeper, not just a ranking signal.
**Freshness** — Pages accessible to GPTBot, ClaudeBot, PerplexityBot. AI Overviews favors recently crawled content.
**FAQPage schema** — Pages with FAQPage schema appear in AI Overviews 3.2× more often (SearchAtlas data).
## Content Structure for AEO
### Answer-First Architecture
Every section should follow the inverted pyramid:
```
┌──────────────────────────────────┐
│ Direct answer (first 1-2 sentences) │ ← Most extractable
├──────────────────────────────────┤
│ Supporting details / evidence │ ← Reinforces citation confidence
├──────────────────────────────────┤
│ Background / context │ ← For human readers
└──────────────────────────────────┘
```
### Structural Requirements
- **One clear H1** aligned with the primary topic
- **Question-based H2s** — Write headings as standalone user questions ("How does X work?")
- **Answer in the opening sentence** — Every section's first sentence directly answers the heading
- **Short paragraphs** — 2-4 sentences max
- **Consistent terminology** — One term per concept; don't rotate synonyms (helps entity recognition)
- **TL;DR / Summary block** — Quick-extractable answer at the top of the article
- **FAQ sections** — Self-contained Q&A pairs matching how users query AI assistants
- **Lists and tables** — LLMs efficiently extract structured data from ordered/unordered lists
- **Quote-worthy claims** — Key numbers in the first sentence of their paragraph; specific, confident, unhedged language
### What NOT to Do
- Long intros / historical context before the answer
- Delaying the answer to increase time-on-page (penalized by LLMs)
- Creative/metaphorical headings ("The Dance of Algorithms")
- Redundant restatements of the same point
- Relying solely on schema to compensate for vague content
## AEO-Specific Structured Data
| Schema Type | AEO Impact | Priority |
|-------------|-----------|----------|
| **FAQPage** | 3.2× more citations in AI Overviews. LLMs directly extract Q&A pairs. | Highest |
| **HowTo** | Step-by-step instructions extracted for procedural queries | High |
| **Article / TechArticle** | Signals content type, authorship, publication date | Required |
| **Organization** | Establishes publisher identity and authority | Required |
| **Person** | Author credentials and authority signals | High |
| **BreadcrumbList** | Helps LLMs understand site hierarchy | Medium |
**Key rules:**
- JSON-LD is the preferred format
- Schema must match visible content exactly (misleading schema damages trust)
- Pages using 3+ relevant schema types show ~13% higher citation likelihood (AirOps)
- FAQPage schema provides compounding benefits when paired with FAQ formatting in visible content
## Ethan Smith / Graphite's AEO Framework
### The 5% Principle
Only ~5% of SEO/AEO strategies drive outsized results. Process: Generate Ideas → Test & Evaluate → Reproduce Results.
### High-Impact AEO Tactics (the 5%)
1. **New AEO landing pages** — Create pages for topics you don't cover
2. **Content enhancement** — Fill answer gaps on existing pages
3. **Citation optimization** — Get mentioned on the most-cited URLs for target topics
### Biggest Waste of Time
Technical AEO (page speed, crawl errors) is low-impact. Priorities: content quality > question coverage > authority > technical tweaks.
### AEO Topics (Not Keywords)
An AEO topic = a cluster of questions targeting a single page. Questions have head, mid-tail, and long-tail varieties. Focus on "Product Questions" — those where answers suggest products or brands.
### Owned vs. Earned
- **Owned** (SEO-like): Directly ranking your page — more effective for specific, product-oriented questions
- **Earned**: Being cited as a source within the LLM's answer — more critical for general, high-level questions
### The 7-Step AEO Playbook
1. Identify target AEO topics (question clusters)
2. Audit current content for answer gaps
3. Create or enhance pages with answer-first structure
4. Add structured data (FAQPage, Article, etc.)
5. Build off-site authority (Reddit, YouTube, guest content)
6. Measure citation rate and iterate
7. Scale what works, kill what doesn't
## LLM-Friendly Content Formats
### llms.txt
A markdown file at the site root providing LLMs with background, guidance, and links. Functions as a foundational AEO element — provides curated context for LLMs, reduces hallucinations, and controls how your site is understood.
**Structure:**
```markdown
# Site Name
> Brief description
## Pages
- [Page Title](URL)
## Optional
- [Full content](llms-full.txt)
```
### Content Negotiation (Accept: text/markdown)
Serve Markdown to LLM agents and HTML to browsers. The agent sends `Accept: text/markdown` in the HTTP header; the server returns clean Markdown. Standards-compliant — no separate URL needed.
**Hugo:** Already implemented on example.com — custom output format renders pages as Markdown. Serve via Hugo's built-in output format routing.
**Ghost:** Needs a reverse proxy (Nginx/Cloudflare Worker) or Ghost API-based solution. Ghost doesn't natively support content negotiation.
### Robots.txt for AI Crawlers
Allow GPTBot, ClaudeBot, PerplexityBot, Google-Extended, Applebot-Extended:
```txt
User-agent: GPTBot
Allow: /
User-agent: ClaudeBot
Allow: /
User-agent: PerplexityBot
Allow: /
User-agent: Google-Extended
Allow: /
User-agent: Applebot-Extended
Allow: /
```
## Measurement
### Core AEO Metrics
| Metric | What It Measures | How |
|--------|-----------------|-----|
| Citation Rate | How often AI cites your content | Manual LLM queries; tools like LLMrefs, AirOps Insights |
| AI Share of Voice | Brand mention frequency vs. competitors | Same prompt set across multiple LLMs |
| Query Coverage | Range of questions where content appears | Expand query into sub-questions; test each |
| AI Referral Traffic | Clicks from ChatGPT, Perplexity, etc. | GA4 source/medium reports |
| AI Overview Appearances | Visibility in Google AI Overviews | GSC → Search Appearance → AI Overviews |
| AEO Readiness Score | Site-level technical AEO readiness | AEOprobe, ansly scanners |
### Cadence
- **Weekly:** Test 5-10 key prompts per site against major LLMs
- **Monthly:** Citation rate audit, brand mention accuracy check
- **Quarterly:** Full AEO audit (structure, schema, llms.txt, crawlability)
### Key References
- Ethan Smith / Graphite: graphite.io/five-percent/aeo-is-the-new-seo
- Lenny's Podcast: "The ultimate guide to AEO" (Sept 2025)
- AirOps 2026 State of AI Search
- SearchAtlas: FAQPage schema → 3.2× AI Overview citations
- Ziptie.dev: 96% of AI Overviews cite already-trusted sources
- HubSpot: Page with 85 AI citations had only 1 backlink
@@ -0,0 +1,142 @@
# Content Strategy SEO Reference
Content strategy SEO ensures that what you write positions you to be discovered for the queries your audience actually searches for.
## Topic Clusters & Pillar Pages
The modern SEO content architecture replaces the old model of writing individual articles about individual keywords. Instead, content is organized into **topic clusters** centered on **pillar pages**.
### How It Works
- **Pillar page:** A comprehensive, long-form guide to a broad topic (e.g., "The Complete Guide to AI Agent Memory Systems"). It covers the topic broadly and links out to cluster content.
- **Cluster content:** Specific articles that dive deep into sub-topics (e.g., "What is a Vector Database?", "How RAG Works", "GraphRAG vs Vector Search"). Each cluster article links BACK to the pillar page.
- **Internal linking:** Cluster → pillar (always). Pillar → cluster (when relevant).
### Why It Works
- **Topical authority:** A group of pages all linking to each other around a common topic signals deep expertise to search engines
- **SERP dominance:** Instead of competing for one keyword, you compete for an entire topic area
- **User experience:** Readers naturally flow from intro content (pillar) to deep dives (clusters)
### Applying to Platform-specific implementation notes
**example.com — Example clusters:**
- Pillar: "AI Agent Memory Systems"
- Clusters: "Vector Databases for Agent Memory," "Knowledge Graphs vs Vector Search," "a content inventory Thought-Graph Architecture," "What is GraphRAG?"
**example.com — Example clusters:**
- Pillar: "Enterprise AI Agent Deployment"
- Clusters: "RAG at Scale: Lessons from Production," "AI Agent Orchestration," "Security Considerations for Enterprise AI," "Cost Optimization for LLM Inference"
## Keyword Research
### Process
1. **Identify seed keywords** — the 3-5 core terms that define your site's domain
2. **Expand via SERP analysis** — search each seed keyword and analyze the "People also ask" box, related searches at page bottom, and the top-ranking pages' headlines
3. **Identify question-based queries** — "how does X work", "what is Y", "why Z matters" — these are excellent for featured snippet opportunities
4. **Analyze search intent** — is the query informational (learning), navigational (finding a specific site), commercial (comparing options), or transactional (buying)? Match your content type to intent.
5. **Assess competition** — are the top 10 results thin blog posts or deep authoritative guides? If the top results are weak, there's an opportunity.
### Keyword Mapping
Map each article to 1 primary keyword and 2-5 secondary/related keywords:
```
Article: "The Artifact Pyramid: Progressive Disclosure for Agent Outputs"
Primary: "artifact pyramid"
Secondary: "progressive disclosure AI", "multi-agent output structure",
"agent collaboration format", "research output pyramid"
```
### Tools
- Google Search Console (what queries your site already ranks for)
- Google "People also ask" / related searches (free, authoritative)
- AnswerThePublic (question-based query discovery)
- Ahrefs / Semrush (paid — comprehensive keyword data)
## SERP Feature Targeting
Target specific SERP features by structuring content appropriately:
| SERP Feature | Best for | Content Structure |
|-------------|----------|-------------------|
| **Featured Snippet (Paragraph)** | "What is X" questions | Direct answer in first paragraph after H2. 40-50 words. |
| **Featured Snippet (List)** | "Steps to X", "Types of X" | Numbered or bulleted list in the content. |
| **Featured Snippet (Table)** | Comparisons, specifications | HTML table with clear headers and data. |
| **FAQ Rich Result** | Multiple related questions | FAQPage schema with 2+ Q&A pairs visible in content. |
| **HowTo Rich Result** | Tutorials, guides | HowTo schema with numbered steps. |
| **People Also Ask** | Question-based queries | Address each question in its own H2 section. |
| **Knowledge Panel** | Brand/organization queries | Organization schema, Wikipedia entry, verified social profiles. |
## Content Gap Analysis
### Process
1. **Identify your target keywords** (the terms you WANT to rank for)
2. **Check your current rankings** (do you already have content for these?)
3. **Analyze top 10 results** for each keyword:
- What content format are they using? (listicle, guide, video, tool)
- How long is the content?
- What angle do they take?
- What's missing from their coverage?
4. **Identify gaps:** Topics where your site has no content, or where your content is weaker than competitors'
5. **Prioritize:** Volume × difficulty × relevance — focus on high-volume gaps that you can credibly fill
### Gap Types
- **Missing topic:** No content exists on your site for a search-worthy topic
- **Thin content:** Content exists but is significantly weaker than competitors'
- **Outdated content:** Content exists but is no longer accurate or current
- **Format gap:** Competitors rank with a format you haven't used (e.g., video, interactive tool, data study)
## Topical Authority
Topical authority is built over time by publishing a breadth and depth of content on a subject. It's the most defensible SEO strategy because it can't be replicated quickly.
### Building Topical Authority
1. **Publish the pillar page first** — broad, comprehensive, definitive
2. **Publish cluster content regularly** — 2-3 sub-topic articles per month
3. **Interlink systematically** — every cluster article links to the pillar; the pillar links to clusters
4. **Refresh content** — update pillar pages annually, cluster content as needed
5. **Expand scope** — once you've covered the core topic, expand to adjacent topics
### Signals of Topical Authority
- Your content ranks for multiple related keywords
- Your content appears in "People also ask" for the topic
- Other sites link to your content as a reference
- Your content is cited in academic or industry publications
## Platform-specific implementation notes — SEO Opportunity Assessment
| Site | Domain Authority | Primary Keyword Focus | Largest Gap |
|------|-----------------|----------------------|-------------|
| example.com | Low (newer personal blog) | AI philosophy, neurodiversity, engineering | Few internal links between related posts; no topic cluster structure |
| example.com | Low (newer enterprise AI blog) | Enterprise AI strategy, agentic AI | Very new — needs pillar pages and cluster strategy from the start |
| example.org | Very low (community site) | Mesh networking, MeshCore, RDUMesh | Local SEO for Raleigh/Durham; community resource queries |
| example.net | Minimal | Southeast mesh networking | Mostly informational — just needs proper on-page SEO |
### Recommended Actions by Site
**example.com:**
- Implement topic clusters retroactively (group existing posts into 3-4 clusters with pillar pages)
- Ensure every post links to 2-3 other posts
- Add FAQPage schema to posts that answer multiple questions
- Optimize for "People also ask" by structuring content around questions
**example.com:**
- Plan pillar pages first before creating more isolated content
- Target question-based keywords for featured snippet opportunities
- Implement TechArticle schema on all technical posts
- Focus on long-tail, low-competition keywords in the short term
**example.org:**
- Local SEO: "mesh network Raleigh," "MeshCore North Carolina"
- Community-Q&A content for FAQ rich results
- Event pages for meetup/community gathering SEO
## Content Calendar
SEO content strategy produces a **ranked list** of content opportunities, not a dated calendar. Editorial schedules vary; prioritize by evidence rather than a fixed calendar. Prioritize by:
1. **Search volume × relevance** — How many searches × how well it fits the site
2. **Competition gap** — How much better can we be than the current top results?
3. **Ease of creation** — Does the research already exist in the vault or is it a new domain?
4. **Pillar dependency** — Should this be created before or after the pillar page?
+204
View File
@@ -0,0 +1,204 @@
# Ghost CMS Metadata & Schema Completion
For Ghost CMS sites (example.com, example.org, example.net), the SEO specialist is responsible for completing all metadata on every published or draft article. This covers three distinct layers: Ghost CMS metadata fields, social media cards, and code-injected structured data.
## Layer 1: Ghost CMS Metadata Fields
Every Ghost post and page has a set of metadata fields in the post settings panel (or settable via the Ghost Admin API / ghost-cli). These must be filled for every article.
### Fields
| Field | Required | Purpose | Best Practice |
|-------|----------|---------|---------------|
| **Meta Title** | Always | Overrides the post title for SEO. Controls the `<title>` tag. | 50-60 chars, primary keyword front-loaded. If not set, Ghost uses the post title (which may be too long or lack keyword focus). |
| **Meta Description** | Always | Overrides excerpt for SEO. Controls the meta description tag. | 150-160 chars, includes primary keyword + CTA, reads naturally. |
| **Custom Excerpt** | Always | Used in card previews, RSS feeds, and as fallback for meta description. | 1-2 sentences capturing the article's core argument. Shorter than meta description (~120 chars). NOT the same as meta description — serves different contexts (card previews, not SERPs). |
| **OG Title** | Recommended | Overrides the title for social sharing (Facebook, LinkedIn, Discord). | Defaults to meta title. Only needed if the social version should differ. |
| **OG Description** | Recommended | Overrides the description for social sharing. | Defaults to meta description. Only needed if the social version should differ. |
| **OG Image** | Always | The image that appears in social card previews. | Should be the article's feature/cover image. If not set, Ghost uses the feature image from the post. Set explicitly to ensure correct crop and fallback. |
| **Twitter Title** | Recommended | Overrides the title for Twitter/X card previews. | Defaults to OG title. Only needed if the Twitter version should differ. |
| **Twitter Description** | Recommended | Overrides the description for Twitter/X. | Defaults to OG description. |
| **Twitter Image** | Recommended | Overrides the image for Twitter/X. | Defaults to OG image. Twitter's card crop differs from OG — set explicitly if the feature image crop doesn't work well as a square. |
| **Canonical URL** | As needed | Overrides the canonical URL. | Only needed if the post is syndicated or republished from another source. Ghost auto-generates self-referencing canonicals. |
| **Slug** | As needed | URL path. | Set before publish. Never change after publish without a 301 redirect. |
### Setting via ghost-cli
The `ghost-cli` tool has a `meta set` command for individual fields:
```bash
# Set meta title and description
ghost-cli --site example meta set <slug> \
--meta-title "Primary Keyword Context | Groktopus" \
--meta-description "150-160 char summary with keyword and call to action."
# Set OG and Twitter fields
ghost-cli --site example meta set <slug> \
--og-title "Optional: different from meta title" \
--og-description "Optional: different from meta description" \
--twitter-title "Optional: different from OG title"
```
### Setting via Ghost Admin API
For bulk operations or automation, use the Admin API directly. The key fields in the post object are:
```json
{
"posts": [{
"id": "post-id",
"meta_title": "SEO Title",
"meta_description": "SEO Description",
"custom_excerpt": "Brief excerpt for cards",
"og_image": "https://example.com/image.jpg",
"og_title": "Social Title",
"og_description": "Social Description",
"twitter_image": "https://example.com/twitter-image.jpg",
"twitter_title": "Twitter Title",
"twitter_description": "Twitter Description",
"canonical_url": null,
"codeinjection_head": "<script type=\"application/ld+json\">...</script>",
"codeinjection_foot": ""
}]
}
```
**Important:** When updating a post via PUT, you must include `updated_at` from the current post data — Ghost uses it for optimistic locking. Always fetch first, modify, then PUT.
## Layer 2: Social Media Cards
Social media cards control what appears when an article is shared on Facebook, LinkedIn, Discord, Twitter/X, Slack, and other platforms.
### How Ghost Generates Cards
Ghost automatically generates Open Graph and Twitter Card meta tags from the post's metadata fields:
```html
<meta property="og:site_name" content="Groktopus">
<meta property="og:type" content="article">
<meta property="og:title" content="[OG Title or Meta Title or Post Title]">
<meta property="og:description" content="[OG Description or Meta Description or Excerpt]">
<meta property="og:image" content="[OG Image or Feature Image]">
<meta property="og:url" content="[Post URL]">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="[Twitter Title or OG Title or Meta Title]">
<meta name="twitter:description" content="[Twitter Description or OG Description or Meta Description]">
<meta name="twitter:image" content="[Twitter Image or OG Image or Feature Image]">
<meta name="twitter:site" content="@example">
<meta name="twitter:creator" content="@example">
```
### Fallback Chain
- **OG Title:** OG Title → Meta Title → Post Title
- **OG Description:** OG Description → Meta Description → Custom Excerpt
- **OG Image:** OG Image → Feature Image
- **Twitter Title:** Twitter Title → OG Title → Meta Title → Post Title
- **Twitter Image:** Twitter Image → OG Image → Feature Image
### Verification
```bash
# Check what tags are being emitted
curl -s https://example.com/<slug>/ | grep -E 'og:|twitter:'
# Test with validators
# Facebook: https://developers.facebook.com/tools/debug/
# Twitter: https://cards-dev.twitter.com/validator
# LinkedIn: https://www.linkedin.com/post-inspector/
```
## Layer 3: Code-Injected Schema
Ghost CMS auto-generates basic Article/BlogPosting schema, but custom schema types (TechArticle, FAQPage, HowTo, Organization with areaServed) must be code-injected.
### Injection Points in Ghost
| Location | Scope | Method |
|----------|-------|--------|
| **Site-wide** (`ghost_head`) | Every page on the site | Settings → Code Injection → Site Header |
| **Per-post** | Single post only | Post Settings → Code Injection → Post Header |
| **Per-page** | Single page only | Page Settings → Code Injection → Post Header |
| **ghost-cli** | Per-post via API | `ghost-cli --site <name> schema inject <slug> --file <path>` |
### Per-Page Schema via ghost-cli
The ghost-cli `schema inject` command handles the API call and stores the schema in the post's `codeinjection_head` field:
```bash
# Validate and inject FAQPage schema
ghost-cli --site example schema validate --json '{"@context":"https://schema.org","@type":"FAQPage","mainEntity":[{"@type":"Question","name":"Q?","acceptedAnswer":{"@type":"Answer","text":"A."}}]}'
ghost-cli --site example schema inject my-post --file /tmp/faq-schema.json
```
### Injecting Multiple Schema Types with @graph
When a post benefits from multiple schema types (e.g., TechArticle + FAQPage), wrap them in a `@graph` array:
```json
{
"@context": "https://schema.org",
"@graph": [
{
"@type": "TechArticle",
"headline": "Post Title",
"proficiencyLevel": "Advanced",
"about": {"@type": "Thing", "name": "AI Agent Memory"}
},
{
"@type": "FAQPage",
"mainEntity": [
{"@type": "Question", "name": "Q1?", "acceptedAnswer": {"@type": "Answer", "text": "A1."}},
{"@type": "Question", "name": "Q2?", "acceptedAnswer": {"@type": "Answer", "text": "A2."}}
]
}
]
}
```
### Schema Coverage by Site
| Site | Required Schema | Injection Method |
|------|----------------|------------------|
| **example.com** | Organization (site-wide), TechArticle (per-post), FAQPage (when applicable) | ghost-cli `schema inject` for per-post; Code Injection → Settings for site-wide |
| **example.org** | Organization + areaServed (site-wide), Article (auto), FAQPage (FAQ content), LocalBusiness if applicable | Same pattern |
| **example.net** | Organization (site-wide), Article (auto) | Same pattern |
### Validation
Every schema injection must be validated:
1. **Google Rich Results Test:** https://search.google.com/test/rich-results
2. **Schema.org Validator:** https://validator.schema.org/
3. **Manual check:** View page source → search for `application/ld+json`
## Completion Checklist
For every article publish:
- [ ] **Meta Title** set (50-60 chars, keyword front-loaded)
- [ ] **Meta Description** set (150-160 chars, includes keyword + CTA)
- [ ] **Custom Excerpt** set (1-2 sentence summary for cards)
- [ ] **OG Image** set (article feature image or explicit OG image)
- [ ] **OG Title** verified (at minimum, confirm it defaults acceptably)
- [ ] **OG Description** verified (at minimum, confirm it defaults acceptably)
- [ ] **Twitter card** type confirmed (summary_large_image)
- [ ] **Canonical URL** confirmed (self-referencing unless syndicated)
- [ ] **Schema injected** (TechArticle for example.com, FAQPage if applicable)
- [ ] **Schema validated** (Google Rich Results Test passes)
- [ ] **Feature image** has alt text and caption
- [ ] **Slug** is optimized (30-45 chars, primary keyword, stable)
## Post-Publish Verification
```bash
# 1. Check meta tags render
curl -s https://<site>/<slug>/ | grep -E '<title|name="description"|property="og:|name="twitter:'
# 2. Check schema renders
curl -s https://<site>/<slug>/ | grep 'application/ld+json'
# 3. Validate schema (requires the JSON block)
# Pipe the extracted JSON-LD to the Rich Results Test API
```
+154
View File
@@ -0,0 +1,154 @@
# On-Page SEO Reference
On-page optimization ensures each piece of content is structured and written to communicate relevance to search engines while serving the reader.
## Title Tags
The title tag is the single most important on-page SEO element. It appears in SERPs as the clickable headline.
### Best Practices
- **Length:** 50-60 characters (Google typically displays the first 50-60 chars; titles longer than ~580px get truncated)
- **Keyword placement:** Primary keyword near the beginning (front-loaded)
- **Uniqueness:** Every page has a unique title tag — no duplicates
- **Branding:** Include site/brand name at the end (separated by `—` or `|` in SERPs, but per the publication style rule, NO emdashes in prose — for title tags, use `|` as separator)
- Example: `"SEO Audit for Ghost CMS Sites | Groktopus"`
- Example: `"The Artifact Pyramid: Progressive Disclosure for Agent Outputs | Example Author"`
- **Compelling:** Includes a value proposition or hook that earns the click
- **No keyword stuffing:** Sounds natural, not like a list of keywords
### Platform-specific implementation notes — Patterns
**example.com (Hugo, PaperMod theme):**
- Title set in frontmatter: `title: "..."`
- Hugo auto-generates `<title>` from the title field
- Verify: `title` in frontmatter is 50-60 chars
- Site name appended automatically by PaperMod: `"Title | Example Author"`
**example.com (Ghost Pro):**
- Meta title set in Ghost post settings → Meta Data → Custom Meta Title
- Default: Post title (which may be too long — always set a custom meta title)
- Ghost appends site name automatically in the `<title>` tag
- Verify: custom meta title under 60 chars
**example.org / example.net (Ghost, self-hosted):**
- Same as example.com — custom meta title in Ghost post settings
## Meta Descriptions
Not a direct ranking factor, but the second most important element for click-through rate from SERPs.
### Best Practices
- **Length:** 150-160 characters (longer descriptions may be truncated)
- **Includes primary keyword + secondary keyword naturally**
- **Includes a call to action** ("Learn how...", "Discover why...", "Read the analysis")
- **Unique per page** — no duplicate or auto-generated descriptions
- **Matches search intent** — if someone searches for "progressive disclosure for agents," the description should signal that the page delivers on that query
- **Contractions and natural language** — reads like a person wrote it
### Platform-specific implementation notes — Patterns
**example.com (Hugo, PaperMod):**
- Hugo auto-generates meta description from page summary/content if not specified
- Always set a `description:` field in frontmatter that's 150-160 chars
- This is the meta description and also used in card previews
**example.com (Ghost Pro):**
- Custom Meta Description in post settings → Meta Data
- Ghost also uses this for Open Graph description
- If not set, Ghost uses the post excerpt (which may be longer or shorter than ideal)
## Heading Structure
Headings communicate content hierarchy to search engines and provide scanability for readers.
### Hierarchy Rules
- **H1:** Exactly one per page. Should match the title tag (or be a slightly more readable version). Contains the primary keyword.
- **H2:** Major sections of the content. Each H2 should contain a related keyword or subtopic. Used as navigation anchors.
- **H3-H6:** Subsections under H2s. Deeper hierarchy for complex content.
- **No skipping levels:** Don't jump from H1 to H3. Hierarchically nested.
### Checklist
- [ ] Exactly one H1 per page
- [ ] H1 matches or closely relates to the title tag
- [ ] H1 contains primary keyword
- [ ] Headings form a logical outline of the page when read alone
- [ ] No empty headings or headings used purely for styling
- [ ] Keywords appear naturally in headings (not stuffed)
- [ ] H2s and H3s are descriptive, not generic ("Introduction" is weak; "Why Progressive Disclosure Matters for AI Agents" is strong)
## Content Quality
### Keyword Usage
- **Primary keyword appears in:** H1, first paragraph, at least one H2, URL slug
- **Keyword density:** Natural usage — don't target specific percentages. If the keyword appears naturally 3-5 times in a 1500-word article, that's fine.
- **LSI / related keywords:** Include semantically related terms that help establish topical relevance (e.g., for an article about "artifact pyramids," include terms like "progressive disclosure," "multi-agent pipelines," "agent collaboration")
- **No keyword stuffing:** Don't repeat the same phrase unnaturally
### Content Length
- **Blog posts:** 1500-2500 words is typical for long-form content. long-form articles often run longer because they're deep analytical pieces. Longer is fine if every word earns its place.
- **Minimum to rank:** 300 words for very simple queries; 1000+ for competitive terms
- **Quality over quantity:** A tight 800-word post that answers the query completely beats a padded 2000-word post that repeats itself
### Readability
- Short paragraphs (2-4 sentences for web reading)
- Bullet points and numbered lists for scannable information
- Bold key terms for emphasis (sparingly)
- Clear section breaks with descriptive headings
### Freshness
- Update dates on evergreen content when significantly revised
- Add "Last updated" or "Updated" notation for major content refreshes
- Google favors freshness for certain query types (news, recent events, technology)
## Internal Linking
Internal links distribute page authority throughout the site and help crawlers discover content.
### Best Practices
- **Link to related content:** Every post should link to 2-5 other posts/pages on the same site
- **Descriptive anchor text:** Use the target topic's keyword as the link text (not "click here" or "read more")
- **Link to cornerstone content:** Important pillar pages should receive more internal links
- **Natural placement:** Links should serve the reader — if it genuinely helps to read more about X, link it
- **Avoid:** Links in navigation that aren't needed, links to the same target with different anchor text, links on every instance of a term
### Platform-specific implementation notes — Patterns
**example.com:** Magnus uses [[wikilinks]] in draft which Hugo converts to hyperlinks. Check that wikilinks are rendering as live HTML links and pointing to existing pages.
**example.com (Ghost):** Manual internal links in the editor. Verify that linked posts exist and are published.
## Image Optimization
### Alt Text
- **Purpose:** Accessibility for screen readers + context for search engines (Google Images)
- **Every image** must have a descriptive alt attribute
- **Descriptive:** "Close-up of a Portia labiata spider's principal eyes showing the characteristic three-lens system" — not "Portia spider" or "image001.jpg"
- **Keyword-optimized:** Include relevant keywords naturally when they describe the image
- **No keyword stuffing:** Alt text is first for accessibility, second for SEO
- **Decorative images:** alt="" (empty alt) for purely decorative images so screen readers skip them
### File Names
- Descriptive, hyphenated: `portia-spider-eyes-closeup.jpg` not `IMG_4732.jpg`
- Include target keyword when appropriate
- Use hyphens, not underscores
### File Size
- Compress images before upload (target < 100KB for standard inline images)
- Use next-gen formats: WebP (with JPEG fallback for older browsers)
- Hugo: Use `.WebP` processing or serve via CDN that auto-converts
- Ghost: Compress before uploading — Ghost does minimal image optimization
## URL Structure
### Best Practices
- **Short, descriptive:** `/seo-audit-ghost-cms/` not `/post/12345/`
- **Include primary keyword:** When natural
- **Hyphens, not underscores:** Google treats hyphens as word separators
- **Lowercase:** /seo-audit not /SEO-Audit
- **Stop words:** Remove unnecessary "and", "the", "of", "for" where they don't add meaning
- **Stable:** Once published, never change a URL (breaks all inbound links)
### Platform-specific implementation notes — Patterns
- **example.com:** Hugo uses the post slug from frontmatter or filename. Verify slug is short and contains primary keyword.
- **example.com:** Ghost auto-generates slug from title. Set a custom slug in post settings if the auto-generated one is too long or doesn't contain the keyword.
- **Never change published slugs** without explicit approval from the site owner.
+244
View File
@@ -0,0 +1,244 @@
# Schema Markup / Structured Data Reference
Structured data helps search engines understand the content of a page and enables rich results in SERPs (FAQ snippets, HowTo steps, breadcrumbs, article previews).
## JSON-LD Format
Google's preferred format. Structured data is typically JSON-LD injected through the CMS, application templates, or a supported code-injection mechanism.
### Basic Structure
```json
{
"@context": "https://schema.org",
"@type": "Article",
"headline": "Title of the Article",
"description": "150-160 char meta description",
"author": {
"@type": "Person",
"name": "Example Author"
},
"datePublished": "2026-05-01",
"dateModified": "2026-05-15",
"image": "https://example.com/image.jpg",
"publisher": {
"@type": "Organization",
"name": "Groktopus",
"logo": {
"@type": "ImageObject",
"url": "https://www.example.com/favicon.png"
}
}
}
```
## Schema Types for Platform-specific implementation notes
### TechArticle (example.com)
Use for technical/analytical content about AI, enterprise technology, and engineering.
```json
{
"@context": "https://schema.org",
"@type": "TechArticle",
"headline": "Article Title",
"description": "Description",
"author": {
"@type": "Person",
"name": "Example Author"
},
"datePublished": "2026-05-01",
"dateModified": "2026-05-15",
"proficiencyLevel": "Advanced",
"about": {
"@type": "Thing",
"name": "Topic area"
}
}
```
### Article / BlogPosting (example.com)
Use for personal blog and analytical long-form content.
```json
{
"@context": "https://schema.org",
"@type": "BlogPosting",
"headline": "Title",
"description": "Description",
"author": {
"@type": "Person",
"name": "Example Author"
},
"datePublished": "2026-05-01",
"dateModified": "2026-05-15"
}
```
For articles authored by Jasper:
```json
{
"@context": "https://schema.org",
"@type": "BlogPosting",
"headline": "Title",
"description": "Description",
"author": {
"@type": "Person",
"name": "Jasper"
},
"datePublished": "2026-05-01",
"dateModified": "2026-05-15"
}
```
### FAQPage (All Sites)
Use when the article answers multiple distinct questions. Enables FAQ rich results in SERPs.
```json
{
"@context": "https://schema.org",
"@type": "FAQPage",
"mainEntity": [
{
"@type": "Question",
"name": "Question 1?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Answer text here."
}
},
{
"@type": "Question",
"name": "Question 2?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Answer text here."
}
}
]
}
```
**Requirements for Google rich result eligibility:**
- Minimum 2 questions
- Questions must be visible text on the page (not hidden)
- Each Question must match visible content
- Answers must be clearly visible to the user (not just in schema)
- Google may show up to 4 FAQ entries in the SERP
### HowTo (Tutorials, Technical Guides)
Use for step-by-step guides and tutorials on any site.
```json
{
"@context": "https://schema.org",
"@type": "HowTo",
"name": "How to Title",
"description": "Description of the tutorial",
"step": [
{
"@type": "HowToStep",
"position": 1,
"name": "Step 1",
"text": "Description of step 1."
},
{
"@type": "HowToStep",
"position": 2,
"name": "Step 2",
"text": "Description of step 2."
}
]
}
```
### BreadcrumbList (Site Navigation)
Use on all sites to enable breadcrumb rich results in SERPs.
```json
{
"@context": "https://schema.org",
"@type": "BreadcrumbList",
"itemListElement": [
{
"@type": "ListItem",
"position": 1,
"name": "Home",
"item": "https://www.example.com/"
},
{
"@type": "ListItem",
"position": 2,
"name": "Category",
"item": "https://www.example.com/category/"
},
{
"@type": "ListItem",
"position": 3,
"name": "Article Title",
"item": "https://www.example.com/article-slug/"
}
]
}
```
### Organization (Site-level)
Use on all sites for site-level schema (injected globally, not per-page).
```json
{
"@context": "https://schema.org",
"@type": "Organization",
"name": "Site Name",
"url": "https://www.example.com/",
"logo": "https://www.example.com/favicon.png",
"sameAs": [
"https://twitter.com/username",
"https://github.com/username"
]
}
```
## Platform-Specific Implementation
### Ghost CMS (example.com, example.org, example.net)
**Per-page schema:**
- Ghost injects basic JSON-LD automatically (Article type with headline, dates, author)
- Custom schema (FAQPage, HowTo, TechArticle) goes in:
- Post-level: Settings → Code Injection → Post Header (`<script type="application/ld+json">...</script>`)
- Site-level: Settings → Code Injection → Site Header (BreadcrumbList, Organization)
**Verification:**
- View source → search for `application/ld+json`
- Test with Google Rich Results Test: https://search.google.com/test/rich-results
- Test with Schema.org Validator: https://validator.schema.org/
**Ghost auto-generated schema limitations:**
- Ghost only generates Article/BlogPosting schema by default
- FAQPage, HowTo, TechArticle, BreadcrumbList, Product all require custom injection
- Author info in Ghost's default schema may need enrichment (add author URL, sameAs)
### Hugo (example.com)
**Per-page schema:**
- Add JSON-LD via Hugo template in `layouts/partials/head.html` or `layouts/_default/single.html`
- Use Hugo's `.Params` to inject article-specific values
- Conditional schema: `{{ if .Params.faq }}` for FAQPage, `{{ if .Params.howto }}` for HowTo
**Site-level schema:**
- Organization and BreadcrumbList in the base template (`baseof.html`)
## Validation Checklist
- [ ] JSON is valid (no trailing commas, properly closed braces)
- [ ] Required fields present for each @type
- [ ] URLs are absolute (including https://)
- [ ] Dates are in ISO 8601 format (YYYY-MM-DD or YYYY-MM-DDTHH:MM:SSZ)
- [ ] Author names match the byline
- [ ] @context is set to "https://schema.org"
- [ ] No conflicting or duplicate schema on the page
- [ ] FAQPage content matches visible page content (no hidden answers)
- [ ] Google Rich Results Test passes without errors
- [ ] Schema content is visible to users where required (FAQPage, HowTo)
+6
View File
@@ -0,0 +1,6 @@
# Source index
- **Source repository:** https://github.com/magnus919/hermes-profiles
- **Inspected commit:** `867a555`
- **Imported source directory:** `seo-audit`
- **Porting boundary:** Retained portable methodology, templates, scripts, and references. Removed or generalized Hermes profile, task-orchestration, memory, and rigid response-handoff assumptions.
+99
View File
@@ -0,0 +1,99 @@
# Technical SEO Reference
Technical SEO ensures search engines can find, crawl, interpret, and index your content. If the technical foundation is broken, nothing else matters.
## Crawlability & Indexability
### Robots.txt
- **Purpose:** Directs crawlers which URLs to avoid
- **Check:** Does the site have a robots.txt at `/robots.txt`?
- **Check:** Are important pages accidentally disallowed? `Disallow: /` blocks ALL crawlers
- **Check:** Is the sitemap referenced? `Sitemap: https://example.com/sitemap.xml`
- **Best practice:** Allow CSS/JS files (modern crawlers need them for rendering)
- **Hosted CMS sites:** Ghost CMS auto-generates robots.txt; verify after any config change
### XML Sitemaps
- **Purpose:** Tells crawlers about all pages and their relative importance
- **Check:** Does the sitemap exist and is it referenced in robots.txt?
- **Check:** Are only canonical URLs included? (No pagination params, sort filters, etc.)
- **Check:** Are noindex pages excluded from the sitemap?
- **Check:** Lastmod dates are accurate (not all the same date)
- **Ghost split sitemaps:** Ghost generates `sitemap-posts.xml`, `sitemap-pages.xml`, `sitemap-tags.xml`, `sitemap-authors.xml`. Verify each is present and valid.
- **Hugo:** Verify sitemap.xml is generated with correct `changefreq` and `priority` settings
### Canonical URLs
- **Purpose:** Tells search engines which URL is the authoritative version of a page
- **Check:** Every page has a self-referencing canonical or points to the canonical version
- **Check:** No conflicting canonicals (page A canonicals to B, B canonicals to C)
- **Common issues:** HTTP/HTTPS duplication (canonical must use HTTPS if site is HTTPS), www/non-www duplication, trailing slash inconsistency
- **Ghost:** Canonical is auto-generated from the post/page slug. Verify no slug changes break inbound canonical signals.
### Meta Robots / Noindex
- **Purpose:** Prevents indexing of specific pages
- **Check:** Pages that should be indexed don't have `<meta name="robots" content="noindex">`
- **Check:** Thin pages (tag pages, author pages, pagination) have noindex if they shouldn't rank
- **Check:** Login/admin pages have noindex
## Page Speed
### Core Web Vitals (Google's ranking signals)
- **LCP (Largest Contentful Paint):** < 2.5s (good), 2.5-4.0s (needs improvement), > 4.0s (poor)
- **FID (First Input Delay) / INP (Interaction to Next Paint):** < 100ms (good), 100-300ms (needs improvement), > 300ms (poor)
- **CLS (Cumulative Layout Shift):** < 0.1 (good), 0.1-0.25 (needs improvement), > 0.25 (poor)
### Common Fixes by Platform
**Ghost CMS:**
- Enable lazy loading for images (Ghost 5+ has built-in)
- Use a CDN for image delivery (Ghost Pro includes; self-hosted needs Cloudflare or similar)
- Limit the number of visible posts on home/tag pages (paginate aggressively)
- Disable unused Ghost integrations and background tasks
- Verify the theme isn't loading unused CSS/JS (many Ghost themes are bloated)
**Hugo (example.com):**
- Hugo generates static HTML — inherently fast
- Image processing: use Hugo's built-in image processing to serve appropriately sized images (`.Resize`, `.Fill`, `.Fit`)
- Minify HTML output: `minify: true` in config
- Avoid excessive JavaScript (Hugo sites shouldn't need much JS)
- Verify CSS is not render-blocking (inline critical CSS if needed)
### Tools for Measurement
- Google PageSpeed Insights (lab + field data)
- Lighthouse (Chrome DevTools)
- Web Vitals library (RUM data)
- GTmetrix / Pingdom (third-party)
## Mobile-Friendliness
- **Check:** Responsive design — does the site render correctly at all viewport widths?
- **Check:** Viewport meta tag present: `<meta name="viewport" content="width=device-width, initial-scale=1">`
- **Check:** Font sizes are readable on mobile (min 16px body text recommended)
- **Check:** Tap targets are adequately sized (min 48x48px recommended)
- **Check:** Content not hidden behind unplayable media or unsupported formats
- **Test:** Google's Mobile-Friendly Test
## HTTPS & Security
- **Check:** Valid SSL certificate (not expired, not self-signed)
- **Check:** All resources load over HTTPS (no mixed content warnings)
- **Check:** HSTS header present for repeat visitors (Strict-Transport-Security)
- **Check:** Redirects HTTP → HTTPS (301 permanent redirect)
- **Check:** No security warnings in browser (mixed content, invalid cert)
## International SEO (If Applicable)
- **hreflang tags:** Correctly implemented for multi-language/multi-region sites
- **Check:** Self-referencing hreflang (each page includes its own language tag as well)
- **Check:** No conflicting signals (hreflang says X, redirect says Y)
## Ghost-Specific Technical SEO
| Check | Where to look | Action |
|-------|---------------|--------|
| Canonical URLs | Post settings → canonical URL | Verify self-referencing or correct canonical |
| Meta robots | Code injection → per-page | No thin pages indexed |
| Sitemap | `/sitemap.xml` | Verify split sitemaps present |
| Robots.txt | `/robots.txt` | Verify sitemaps referenced, no accidental disallows |
| Structured data | Source HTML → JSON-LD | Verify Ghost's built-in JSON-LD is correct + any custom |
| AMP | Ghost settings | Verify AMP is enabled/disabled intentionally (Ghost defaults to disabled in v5+) |
| Open Graph / Twitter Cards | Post social settings | Verify Facebook and Twitter card previews |