---
name: frontend-seo
description: >-
Enforces search engine optimization patterns when building HTML pages.
Use when creating new pages, adding meta tags, implementing structured data,
generating sitemaps, writing Open Graph tags, or optimizing for search.
---
# Front-end SEO
Technical SEO is the foundation that determines whether good content gets
found. A page with perfect content but broken meta tags, missing structured
data, or slow load times will lose to a mediocre page that nails the
fundamentals. This skill covers what front-end code controls.
**Priority order** — fix these first, they have the most ranking impact:
1. Page speed (LCP, INP, CLS) — see the `frontend-performance` skill
2. Title tags and meta descriptions (direct ranking factor + CTR)
3. Heading structure and content hierarchy
4. Canonical URLs (prevents duplicate content penalties)
5. Structured data (enables rich results, not a direct ranking factor)
6. Open Graph / Twitter Cards (social sharing, indirect traffic)
## Meta Tags
Every page must include these in `<head>`, in this order:
```html
<title>Primary Keyword — Context or Brand</title>
<meta name="description" content="Actionable description with primary keyword.">
<meta name="robots" content="index, follow">
<link rel="canonical" href="https://example.com/page/">
<meta property="og:type" content="article">
<meta property="og:url" content="https://example.com/page/">
<meta property="og:title" content="Primary Keyword — Context or Brand">
<meta property="og:description" content="Description optimized for social sharing.">
<meta property="og:image" content="https://example.com/og-image.jpg">
<meta property="og:site_name" content="Site Name">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="Primary Keyword — Context or Brand">
<meta name="twitter:description" content="Description optimized for social sharing.">
<meta name="twitter:image" content="https://example.com/og-image.jpg">
```
### Title tag rules
The title tag is the single highest-leverage on-page ranking factor.
- **50-60 characters** — Google truncates at ~60; aim for 55
- **Primary keyword first** — front-loading the keyword has measurable
ranking impact. "Cursor AI Review" beats "A Review of Cursor AI"
- **Unique per page** — duplicate titles signal thin or duplicate content
to crawlers
- **Format**: `Primary Topic — Brand` (use em dash, not pipe or colon)
- **Never** start with the brand name unless the page IS the brand
(homepage)
### Meta description rules
Descriptions don't directly affect ranking but control click-through rate,
which does. Treat them as ad copy for the search results page.
- **150-160 characters** — Google truncates longer descriptions
- **Include primary keyword** — Google bolds matching terms in results
- **Start with a verb or value proposition** — "Learn how...",
"Compare the top...", "Build faster with..."
- **Unique per page** — Google will auto-generate descriptions for pages
with duplicate or missing descriptions, and they're usually worse
### Canonical URL rules
Canonicals prevent duplicate content penalties. A single canonical mistake
can de-index a page.
- Every page must have a self-referencing canonical
- **Absolute URLs only** — `https://example.com/page/`, never `/page/`
- Pick one trailing slash convention and enforce it everywhere
- If content exists at multiple URLs (with/without www, with/without
trailing slash), all canonicals must point to the preferred version
- The canonical URL must return a 200 status code
## Heading Structure
Headings define the document's semantic outline. Search engines use them to
understand content hierarchy and topic structure. See the
`frontend-components` skill for the full heading hierarchy rules.
- Exactly one `<h1>` per page — this is the page's primary topic signal
- `<h1>` should closely match the `<title>` keyword (not identical, but
topically aligned)
- `<h2>` for major sections, `<h3>` for subsections — never skip levels
- Don't choose heading level for font size — use CSS
- Include keywords in headings naturally, not forced
## Structured Data (JSON-LD)
Structured data enables rich results (star ratings, FAQ dropdowns, breadcrumb
trails, article cards). It is not a direct ranking factor, but rich results
dramatically increase click-through rate.
Use JSON-LD format in a `<script type="application/ld+json">` tag. Place it
at the end of `<head>` or before `</body>`.
### Which type to use
| Page type | Schema type | Rich result enabled |
|-----------|-------------|---------------------|
| Homepage | `Organization` + `WebSite` | Sitelinks search box, knowledge panel |
| Blog/news article | `Article` | Article card with date and author |
| Tutorial/guide | `HowTo` | Step-by-step display in results |
| FAQ section on any page | `FAQPage` | Expandable Q&A in results |
| Tool/app page | `SoftwareApplication` | App info panel |
| Any page with breadcrumbs | `BreadcrumbList` | Breadcrumb trail in results |
Apply multiple types when a page qualifies. An article with an FAQ section
should have both `Article` and `FAQPage` schemas.
### Article (most common)
```json
{
"@context": "https://schema.org",
"@type": "Article",
"headline": "Article Title",
"description": "Article description.",
"image": "https://example.com/article-image.jpg",
"datePublished": "2026-03-15",
"dateModified": "2026-03-20",
"author": { "@type": "Person", "name": "Author Name" },
"publisher": {
"@type": "Organization",
"name": "Site Name",
"logo": { "@type": "ImageObject", "url": "https://example.com/logo.svg" }
}
}
```
Always include `dateModified` — omitting it signals stale content. Update it
whenever the page content meaningfully changes.
### BreadcrumbList
```json
{
"@context": "https://schema.org",
"@type": "BreadcrumbList",
"itemListElement": [
{ "@type": "ListItem", "position": 1, "name": "Home", "item": "https://example.com/" },
{ "@type": "ListItem", "position": 2, "name": "Section", "item": "https://example.com/section/" },
{ "@type": "ListItem", "position": 3, "name": "Current Page" }
]
}
```
The last item omits `item` — it represents the current page.
### Organization + WebSite (homepage only)
```json
{
"@context": "https://schema.org",
"@type": "Organization",
"name": "Company Name",
"url": "https://example.com",
"logo": "https://example.com/logo.svg",
"description": "What the organization does.",
"sameAs": ["https://twitter.com/handle", "https://github.com/handle"]
}
```
```json
{
"@context": "https://schema.org",
"@type": "WebSite",
"name": "Site Name",
"url": "https://example.com",
"potentialAction": {
"@type": "SearchAction",
"target": "https://example.com/search?q={search_term_string}",
"query-input": "required name=search_term_string"
}
}
```
Only add `SearchAction` if the site actually has a search feature.
### Validation
Validate structured data with Google's Rich Results Test after every change.
Common errors that silently prevent rich results:
- Missing required fields (`headline` for Article, `name` for HowTo steps)
- Relative URLs in `image`, `url`, or `logo` fields (must be absolute)
- `datePublished` / `dateModified` in wrong format (use `YYYY-MM-DD`)
- Logo image that doesn't meet Google's size requirements (min 112x112px)
## Content Structure
### Internal linking
Internal links distribute page authority and help crawlers discover content.
Weak internal linking is the most common SEO mistake on content-heavy sites.
- Every page must be reachable within 3 clicks from the homepage
- Use descriptive anchor text — "Cursor AI code editor review" not "click
here" or "read more"
- Link to related content within body copy, not just in navigation
- New pages must have at least 2 internal links pointing to them from
existing pages, or they become orphan pages that crawlers may never find
### URL structure
- Lowercase, hyphen-separated: `/tools/cursor/`, not `/Tools/Cursor/`
- Short and descriptive — include primary keyword
- Consistent trailing slash convention
- Avoid query parameters for content pages
- Avoid dates in URLs unless content is genuinely time-sensitive (news
articles that will never be updated)
## Image SEO
See the `frontend-performance` skill for image format and loading
optimization. From an SEO perspective:
```html
<img src="/tools/cursor-editor-interface.avif"
alt="Cursor AI code editor showing inline code suggestions"
width="1200" height="675"
loading="lazy" decoding="async">
```
- **Descriptive filenames**: `cursor-editor-interface.avif`, not
`IMG_1234.jpg` — search engines use filenames as a relevance signal
- **Alt text**: describe what the image shows and why it matters, not what
it is. "Cursor AI code editor showing inline suggestions" beats
"screenshot" or "image of editor"
- **Decorative images**: `alt=""` — screen readers and search engines should
skip them
- **Include `width` and `height`** — prevents CLS, which is a ranking
factor (see `frontend-performance` skill)
## Sitemap
Maintain `sitemap.xml` in the root directory. Include every indexable page.
```xml
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://example.com/</loc>
<lastmod>2026-03-15</lastmod>
<changefreq>weekly</changefreq>
<priority>1.0</priority>
</url>
</urlset>
```
- Update `<lastmod>` only when content meaningfully changes — don't set it
to today's date on every deploy
- Set `<priority>` relative to other pages: homepage 1.0, section indexes
0.8, detail pages 0.6
- Every new page must be added to `sitemap.xml` before it goes live
- Orphan pages in the sitemap (not linked from the site) signal quality
issues to crawlers
## robots.txt
```
User-agent: *
Allow: /
Sitemap: https://example.com/sitemap.xml
```
- Reference the sitemap URL
- Block only pages that should never be indexed (admin, staging)
- **Never block CSS or JS** — search engines need them to render the page
and evaluate content quality
## Mobile-First Indexing
Google uses the mobile version for indexing and ranking. A page that looks
good on desktop but breaks on mobile is ranked based on the broken version.
- Responsive layout at all viewport widths
- Same content on mobile and desktop — don't hide content on mobile with
`display: none` or `visibility: hidden`
- Touch targets at least 48x48px
- Body text at least 16px — text smaller than this triggers Google's
"mobile-friendly" warning
- No horizontal scrolling
## Open Graph Images
OG images determine how a page looks when shared on social media. A good
OG image can 2-3x the click-through rate on social shares.
- **Size**: 1200x630px (this ratio works across all platforms)
- Include site branding and page title as text overlay
- Use a consistent template — brand recognition across shares
- Provide a site-wide default fallback for pages without a specific OG image
- Test with the platform debuggers (Facebook Sharing Debugger, Twitter Card
Validator) after deployment
## Page-Level Checklist
Apply to every new page before it goes live:
- [ ] Unique `<title>` (50-60 chars, primary keyword front-loaded)
- [ ] Unique `<meta name="description">` (150-160 chars, starts with verb)
- [ ] Self-referencing `<link rel="canonical">` with absolute URL
- [ ] Open Graph tags (og:title, og:description, og:image, og:url, og:type)
- [ ] Twitter Card tags (twitter:card, twitter:title, twitter:description,
twitter:image)
- [ ] Appropriate JSON-LD structured data (see decision table above)
- [ ] Single `<h1>` aligned with `<title>` keyword
- [ ] Logical heading hierarchy (no skipped levels)
- [ ] Descriptive alt text on all content images
- [ ] At least 2 internal links pointing to this page from existing pages
- [ ] Page added to `sitemap.xml` with correct `<lastmod>`
- [ ] LCP <= 2.5s (see `frontend-performance` skill)
- [ ] Mobile-friendly layout tested at 375px width
- [ ] Structured data validated with Rich Results Test
## Anti-Patterns
**Never do these:**
- Duplicate `<title>` tags across pages — each page competes independently
- Omit the canonical URL — lets search engines choose the "preferred" URL
for you, often incorrectly
- Use relative URLs in canonical, OG, or structured data — breaks when
pages are served from different paths
- Nest multiple `<h1>` tags — dilutes the primary topic signal
- Use JavaScript-only rendering for critical content — Googlebot can execute
JS but it's a separate, delayed crawl pass with a lower budget
- Block CSS/JS in robots.txt — prevents Googlebot from rendering the page
- Omit `dateModified` in Article structured data — signals stale content
- Create orphan pages with no internal links — crawlers may never find them
- Use "click here" or "read more" as anchor text — wastes a linking signal
- Stuff keywords into headings unnaturally — Google's language models detect
and penalize this
- Set every page's sitemap priority to 1.0 — priority is relative; if
everything is 1.0, nothing is