SuperAPI AI Directory

seo-content-optimization

Marketing & growth

View repository

Updated 10-16 00:03

About this skill

SEO Content Optimization

Audit-and-fix workflow for improving search rankings on Magnus's content sites. This is the "optimize what exists" counterpart to /content-strategy's "what to write next."

Sites in scope: magnus919.com (Hugo), rdumesh.org (Ghost), groktop.us (Ghost Pro), southeastme.sh (Ghost)

Signals to Run This

  • User says "we're not ranking for X" or "SEO is bad"
  • User asks to audit a site's search presence
  • User mentions poor Google visibility for local terms
  • As part of site launch / relaunch

Process

1. Audit page titles and meta descriptions

Fetch the site's sitemap to get all published URLs (see content-strategy skill for sitemap-fetching protocol — try bare domain, www subdomain, Ghost split-sitemaps).

For each published page:

  • Check the <title> tag via curl
    curl -s https://example.com/page/ | grep -i '<title'
    
  • Check meta description
    curl -s https://example.com/page/ | grep -i 'name="description"'
    
  • Evaluate against:
    • Does the title include the primary keyword near the front?
    • Does it include location terms (for local sites)?
    • Is it under 60 characters (Google display limit)?
    • Is the meta description under 160 chars, compelling, and keyword-rich?

2. Audit structured data

Check for existing schema:

// in browser console
document.querySelectorAll('script[type="application/ld+json"]')

For each JSON-LD block, verify:

  • Is there an Organization or LocalBusiness schema?
  • Does it include areaServed with specific cities?
  • Is there FAQPage schema on FAQ content?
  • Is there BreadcrumbList?
  • Is Article schema correctly populated?

3. Inject missing schema

Organization schema — site-wide (Ghost Code Injection → ghost_head). The critical fields for local SEO:

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "Organization",
  "name": "Site Name",
  "description": "Description with city and keyword signals.",
  "url": "https://example.com",
  "areaServed": [
    {"@type": "City", "name": "City1", "sameAs": "https://en.wikipedia.org/wiki/City1,_NC"},
    {"@type": "City", "name": "City2"}
  ],
  "sameAs": [
    "https://discord.gg/..."
  ]
}
</script>

FAQ schema — page-level. Enables rich FAQ results in search. For Ghost sites, use the ghost-cli schema inject command instead of post-level code injection — it's cleaner and doesn't require editing the post HTML:

ghost-cli --site rdumesh schema validate --json '{"@context":"...","@type":"FAQPage",...}'
ghost-cli --site rdumesh schema inject my-post --file /tmp/faq-schema.json

FAQPage JSON-LD structure:

{
  "@context": "https://schema.org",
  "@type": "FAQPage",
  "mainEntity": [
    {
      "@type": "Question",
      "name": "Question text?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Answer text."
      }
    }
  ]
}

TechArticle schema — for hardware/software/technical blog posts. More specific than Article (which Ghost outputs by default), gives Google better content classification. Include proficiencyLevel and about for deeper signals:

{
  "@type": "TechArticle",
  "headline": "Post Title",
  "proficiencyLevel": "Beginner|Intermediate|Advanced",
  "about": {
    "@type": "Thing",
    "name": "Topic name"
  }
}

Hybrid @graph pattern — when a post benefits from multiple schema types (FAQPage + HowTo + TechArticle), wrap them in a @graph array in a single injection:

{
  "@context": "https://schema.org",
  "@graph": [
    {"@type": "TechArticle", "headline": "..."},
    {"@type": "FAQPage", "mainEntity": [...]},
    {"@type": "HowTo", "name": "...", "step": [...]}
  ]
}

This is cleaner than multiple separate JSON-LD blocks and ensures all types are processed together by Google.

When to use each type:

Type Article Content Signal SERP Impact
TechArticle Technical hardware/software content Better classification, no visual rich result
FAQPage 3+ natural Q&A pairs (headings + paragraph answers) Expandable FAQ rich result (high CTR)
HowTo Step-by-step procedures, CLI commands, build instructions Step preview rich result

Fallback for non-Ghost sites: For Hugo sites (magnus919.com), schema is baked into static HTML at build time via Hugo partials — see the magnus919.com section below for implementation approaches. For Ghost sites, always prefer schema inject via ghost-cli over manual code injection.

4. Rewrite page titles (meta_title, not H1)

In Ghost CMS, page titles and H1s are independent. Set meta_title via the Admin API or Ghost post settings — it controls the <title> tag and what shows in search results. The visible H1 on the page stays unchanged.

Pattern: Primary keyword near front | pipe-separated site name suffix. For local sites, include city names. For Ghost sites, use the ghost-cli to set these:

ghost-cli --site rdumesh meta set <slug> \
  --meta-title "Primary Keyword Context | Site Name" \
  --meta-description "150-160 char summary with keyword and call to action."
Type Format Example
Local landing Keyword + city + site "Free Mesh Network for Raleigh, Durham & Chapel Hill, NC
Technical guide Keyword + site "915 MHz Pine Tree Attenuation: What LoRa Mesh Operators Need to Know
Hardware guide Product + keyword + site "Heltec V4.3 Solar Repeater: Power Consumption & EasySkyMesh Firmware
FAQ Keyword + cities + site "Mesh Radio Network FAQ: RDUMesh for Raleigh, Durham, Chapel Hill"
Announcement Topic + site "Chapel Hill Mesh Network Expansion — RDUMesh Backhaul Case Study"

Set via ghost-cli meta set for Ghost sites (see ghost-cli skill) or via Hugo template variables for magnus919.com.

5. Audit and optimize URL slugs

In Ghost CMS, slugs can be changed independently of titles and the ghost-cli doesn't have a dedicated slug command — use the Admin API directly (see ghost-cli skill's "Slug Optimization" section for the pattern). Issues to flag:

  • -2 suffixes (signal duplicate content) — fix slug, ensure 301 redirect from old URL
  • Excessively long slugs (>55 chars) — shorten by removing filler words (why, what, the, a, of, and, how, for)
  • Ghost auto-generates slugs from the initial post title, so if the title changes later, the slug doesn't update
  • Target 30-45 characters, leading with the primary keyword
  • OK to change before publish. Changing after publish requires a 301 redirect.

6. Create local landing pages (if applicable)

For sites serving specific geographic areas, create dedicated short pages (500-800 words) per city/region. These don't need to be deep essays — they exist to tell Google "this site serves this place."

Ghost CMS: always create as Pages, not Posts. Pages don't trigger email notifications to subscribers. Posts do.

Each should include:

  • City name in H1 and first paragraph
  • 2-3 paragraphs of substantive local content (not keyword stuffing)
  • Link to the relevant coverage map or router page
  • CTA to join/participate
  • Internal links to 2-3 other relevant posts

Ghost Admin API pitfall — Ghost v6 uses Lexical format, not raw HTML. When creating pages via the API, sending "html" in the payload does NOT store the content. You must provide "lexical" as a JSON string in Ghost's Lexical editor format. The html field is read-only — derived from Lexical on the backend.

Minimal Lexical page structure for a short page with paragraphs and headings:

lexical = json.dumps({
    "root": {
        "children": [
            {
                "children": [{"detail": 0, "format": 0, "mode": "normal", "style": "", "text": "Paragraph text.", "type": "text", "version": 1}],
                "direction": "ltr", "format": "", "indent": 0, "type": "paragraph", "version": 1
            },
            {
                "children": [{"detail": 0, "format": 0, "mode": "normal", "style": "", "text": "Section Heading", "type": "text", "version": 1}],
                "direction": "ltr", "format": "", "indent": 0, "type": "heading", "tag": "h2", "version": 1
            }
        ],
        "direction": "ltr", "format": "", "indent": 0, "type": "root", "version": 1
    }
})

For ordered lists, use "type": "list", "listType": "number" with "type": "listitem" children.

7. Measure

  • Immediate: curl | grep <title> to confirm titles deployed
  • Schema validation: Google Rich Results Test (https://search.google.com/test/rich-results)
  • Crawl: Google Search Console after 1-2 weeks
  • Ranking: Manual spot-checks for targeted queries (incognito browser)

Site-Specific Notes

rdumesh.org

  • Self-hosted Ghost on Ubuntu. Admin API at same domain as public.
  • Biggest gap: city-specific landing pages and schema with areaServed.
  • FAQ schema on the FAQ page is a high-impact copy-paste.

groktop.us

  • Ghost Pro behind custom domain. Admin API at admin domain groktopus-hybrid-workforce.ghost.io.
  • Enterprise AI audience — local SEO doesn't apply.
  • Focus on meta title optimization and Article schema completeness.

magnus919.com

  • Hugo static site (Terminal theme v4). Currently emits zero JSON-LD. The Terminal theme's head.html partial does not include any schema markup.

  • Hugo has no built-in runtime schema injection (unlike Ghost's ghost_head), but several zero-cost approaches exist:

    Approach Effort Output
    Hugo internal template ({{ template "_internal/schema.html" . }}) Add one line to head.html Basic WebSite (home) + Article (pages) with Organization, dates, author
    Custom partial (layouts/partials/schema.html) Write a Handlebars partial with per-page-kind conditionals Full control over @type, fields, and which pages get which schema
    PaperMod-style partial (see _vendor for reference) Adapt existing schema_json.html from vendored PaperMod theme Organization + BreadcrumbList + BlogPosting with articleBody, wordCount, keywords

    Unlike Ghost's injection-based approach, Hugo schema is baked into static HTML at build time — no runtime overhead, but changes require a rebuild.

  • Site already has good organic content depth; focus on page title optimization and internal linking.

southeastme.sh

  • Nascent site — SEO work is premature. Focus on content strategy first.

LLM-Friendly Content (/llms.txt)

An emerging complement to traditional SEO: making your site consumable by LLM agents during inference. While SEO targets Google's crawler, llms.txt targets the agents that visit your site looking for context.

The standard: A markdown file at /llms.txt providing a curated, concise overview — H1 title, blockquote summary, and H2 sections with link lists. The ## Optional section can be skipped for tighter context.

How it differs from SEO:

  • SEO optimizes for ranking in search results (traffic goal)
  • llms.txt optimizes for LLM comprehension (utility goal)
  • Both require clear, concise content architecture
  • llms.txt is far simpler — one markdown file vs. structured data + meta + backlinks

Quick check when evaluating a site:

  • /llms.txt exists? Read it first.
  • No /llms.txt? Check /sitemap.xml for full page list.
  • Neither? The site forces LLMs to parse HTML — opportunity to create one.

See references/llmstxt-standard.md for the full standard reference — format spec, best practices, implementation approaches for Hugo/Ghost/Next.js, the llms_txt2ctx CLI tool, and content negotiation via Accept: text/markdown.

See Also

  • references/llmstxt-standard.md — full /llms.txt standard reference
  • ghost-webhooks skill reference admin-api-content-editing.md — Admin API auth and update patterns
  • content-strategy skill — what to write next (complements this skill)
  • hugo-blog skill — Hugo-specific llms.txt implementation details (custom output formats, content negotiation)