SEO-Friendly Markup
SEO starts with what your theme outputs: headings, landmarks, navigation, and link structure. GeneratePress is a strong baseline because it favors semantic HTML and minimal, predictable markup.
The Concept: "SEO-Friendly" Is Mostly "Unambiguous"
Search engines do not need fancy HTML. They need HTML that is:
- Semantic (landmarks like
header,nav,main,footer) - Consistent (one primary H1 per page, logical H2/H3 hierarchy)
- Crawlable (links are links, content is not hidden behind scripts)
- Fast to parse (avoid excessive wrappers and inline scripts)
GeneratePress helps because its templates are intentionally lean. Your job is to keep your customizations lean too.
Where Markup Usually Goes Wrong
Common theme + editor pitfalls:
- Multiple H1s (often from a hero block plus the post title)
- Skipping heading levels (H2 -> H4) because of visual styling choices
- Navigation built with
div+ click handlers instead of actual<a>links - Important content placed inside sliders/tabs that are hidden by default
- Duplicate breadcrumbs (theme + SEO plugin + page builder)
Avoid adding "SEO helpers" in multiple places. A single breadcrumb trail, a single schema source, and a single canonical URL source is the safe default.
GeneratePress Basics That Help SEO
Use HTML5 + correct landmarks
GeneratePress outputs semantic structures by default. The key is to keep your custom templates and Elements aligned with that structure.
Keep your headings intentional
Treat headings as document structure, not as a styling tool.
Quick rule of thumb:
- Pages: one H1 (usually the page title)
- Sections: H2
- Subsections: H3
Make navigation "real" navigation
Menus should be <nav> containing <a> links, not interactive blocks that require JS to navigate.
Build internal links like an information architecture
Internal linking is markup + content strategy:
- Use descriptive anchor text (not "click here")
- Link up (child -> parent) and across (related pages)
- Keep key pages reachable within a few clicks
Practical Checklist (Do This Per Page Type)
Use this table as a recurring QA pass when you create layouts with the Customizer, Elements, and blocks.
| Area | What to look for | GeneratePress-friendly approach |
|---|---|---|
| Title + H1 | Exactly one H1, matches page intent | Let WordPress output the title; avoid extra H1 hero blocks |
| Landmarks | header, nav, main, footer present | Avoid wrapping everything in extra divs via Elements |
| Nav links | Links are <a href> | Use WP Menus + GP navigation; don't use JS-only buttons |
| Images | alt describes purpose | Use descriptive alt for content images; empty alt for purely decorative |
| Above-the-fold | Content visible without JS | Avoid hiding primary copy in tabs/accordions by default |
| Pagination | Crawlable next/prev links | Use archive pagination; avoid infinite scroll for SEO-critical archives |
How To Inspect Your Markup Quickly (Server-Side)
On an Ubuntu server (including an OpenLiteSpeed VPS), you can spot-check output without needing browser devtools.
SITE_URL="https://example.com"
curl -sL "$SITE_URL/about/" | sed -n '1,160p'
If you want more targeted checks, look for headings and landmarks:
SITE_URL="https://example.com"
curl -sL "$SITE_URL/" | rg -n "<(h1|h2|main|nav|header|footer)" | sed -n '1,120p'
If a page is built with blocks, your content structure is often fine but the hero section introduces an extra H1. Make the hero headline an H2 and style it, rather than upgrading it to H1.
Adding Breadcrumbs Without Mess
Breadcrumbs can help users and can reinforce internal linking, but they must be consistent and non-duplicated.
If you use an SEO plugin that provides breadcrumbs, prefer that single source. If you are intentionally implementing a simple breadcrumb trail yourself, add it once, close to the title, using a Hook Element.
<?php
/**
* Place this in a GeneratePress Hook Element (PHP enabled),
* or in a child theme where appropriate.
*/
if ( is_front_page() ) {
return;
}
echo '<nav class="breadcrumbs" aria-label="Breadcrumbs">';
echo '<a href="' . esc_url( home_url( '/' ) ) . '">Home</a>';
echo ' <span aria-hidden="true">/</span> ';
echo '<span class="breadcrumbs__current">' . esc_html( wp_get_document_title() ) . '</span>';
echo '</nav>';
Best Practices
- Prefer semantic structure over wrapper divs
- Use CSS to style headings instead of changing heading levels
- Keep a single source of truth for: breadcrumbs, schema, canonicals
- Don't hide primary content behind interactions by default
- Validate templates when you add Elements or custom hooks
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Two H1s on a page | Hero block is H1 + post title is H1 | Change hero headline to H2; keep only the title as H1 |
| Breadcrumbs appear twice | SEO plugin + custom Hook Element | Remove the custom breadcrumb hook or disable plugin breadcrumbs |
| Nav links not crawlable | Menu built with buttons/JS | Use WP Menu + <a> links; avoid click handlers |
| Archives look thin | Excerpts too short + no taxonomy context | Add helpful intro content to category/tag archives |
Hands-On: Markup QA Pass
- Pick one important page (Home, Services, or a core landing page).
- Fetch the HTML and confirm landmarks/headings exist.
- Fix any H1 duplication and heading skips.
- Confirm the primary navigation uses real links.
- Re-test.
PAGE_URL="https://example.com/services/"
curl -sL "$PAGE_URL" > /tmp/page.html
rg -n "<h1" /tmp/page.html
rg -n "<main" /tmp/page.html
rg -n "<nav" /tmp/page.html
Quick Reference
- One page, one H1
- Landmarks:
header,nav,main,footer - Navigation: real
<a href>links - Avoid duplicate breadcrumbs/schema sources
What's Next
- Next: Schema Implementation