HTML and SEO
There is no tag that makes a page rank. Anyone selling one is selling 2004.
What markup does is three things: make a page findable, make it understandable, and make it eligible for the richer result formats. Most of what helps is the same markup that makes a page accessible, because both audiences want the same thing - content whose structure is stated rather than implied.
The foundations
Before any tag: a page has to be worth finding, reachable by a crawler, reasonably fast, and usable on a phone. Markup contributes to the last three.
Crawlable. Real links with real href attributes. A navigation built from div elements with click handlers is invisible to a crawler, which does not click.
Fast. The loading advice from the performance lesson, with layout shift mattering most because it is measured directly.
Usable on a phone. The viewport tag, text that is not inside images, touch targets large enough to hit.
Title, headings, description
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>HTML and SEO: what markup can and cannot do - Omni Academy</title>
<meta name="description"
content="Which HTML actually helps a page be found: titles, headings, canonical links, structured data, and the three claims that are myths.">
<link rel="canonical" href="https://example.com/courses/html/seo">
</head>
<body>
<main>
<h1>HTML and SEO</h1>
<h2>What markup can do</h2>
<h3>Titles and headings</h3>
<h2>What markup cannot do</h2>
</main>
</body>
Open this example in the editor and change it. Nothing you type is saved.
The title is the strongest single signal you control, and it is also the blue line somebody decides to click. Unique per page, specific part first, around 60 characters.
Headings are an outline. One h1 naming the page, h2 for sections, no skipped levels. Search engines summarise from this, and so do answer engines quoting a passage.
The description does not affect ranking. It may become the snippet, and it is frequently rewritten when the engine thinks it can do better for a particular query. Write it anyway: when it is used, it is your sentence rather than an extracted fragment.
Canonical, robots, and the ways to disappear by accident
<link rel="canonical" href="https://example.com/courses/html/seo">
<meta name="robots" content="index, follow, max-snippet:-1, max-image-preview:large">
Open this example in the editor and change it. Nothing you type is saved.
A canonical names which address is the real one when the same content is reachable several ways. It must be absolute, and it should be self-referential on the canonical page.
robots controls indexing. The failure modes are more interesting than the directives:
- A
noindexleft on a page from staging. The single most common cause of a page that never appears. - A canonical pointing at the home page from every page, usually from a template mistake. Every page then claims to be a duplicate of the home page.
- A page blocked in
robots.txtand carryingnoindex. The crawler cannot fetch it, so it never reads thenoindex, so the URL can still appear as a bare link. To remove a page, allow the crawl and usenoindex.
Structured data
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Course",
"name": "HTML: From Basics to Expert",
"description": "Thirty topics, five modules, every example runnable in the browser.",
"provider": { "@type": "Organization", "name": "Omni Academy" },
"educationalLevel": "Beginner"
}
</script>
Open this example in the editor and change it. Nothing you type is saved.
Structured data states facts about the page in a vocabulary search engines parse. JSON-LD in a script block is the recommended form - one block, generatable, validatable, and separate from the markup it describes.
The types worth the effort are few: Article or BlogPosting for writing, Product with Offer for things for sale, FAQPage for questions and answers, BreadcrumbList for hierarchy, Organization once per site, Course for courses.
Two honest limits. It makes a page eligible for a rich result and guarantees nothing. And the data must describe what is actually on the page - marked-up ratings or prices that do not appear are a policy violation, and the penalty is losing rich results entirely.
Breadcrumbs
Worth their own mention because they are the easiest win on a deep site, and because they should exist twice: once as markup people can click, once as structured data.
<nav aria-label="Breadcrumb">
<ol>
<li><a href="/">Home</a></li>
<li><a href="/courses">Courses</a></li>
<li><a href="/courses/html">HTML</a></li>
<li><a href="/courses/html/seo" aria-current="page">SEO</a></li>
</ol>
</nav>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "BreadcrumbList",
"itemListElement": [
{ "@type": "ListItem", "position": 1, "name": "Home", "item": "https://example.com/" },
{ "@type": "ListItem", "position": 2, "name": "Courses", "item": "https://example.com/courses" },
{ "@type": "ListItem", "position": 3, "name": "HTML", "item": "https://example.com/courses/html" }
]
}
</script>
Open this example in the editor and change it. Nothing you type is saved.
The visible version is an ordered list inside a labelled nav, with aria-current="page" on the last item. The structured version lets a search result show the path instead of a bare URL.
Images, links and language
<img src="parse-tree.png"
alt="A browser parses markup into a tree of html, head and body"
width="800" height="450" loading="lazy">
<p>Read the <a href="/courses/html/accessibility">guide to accessibility</a>.</p>
Open this example in the editor and change it. Nothing you type is saved.
alt text is indexed, and it is how image search understands a picture. Write it for a person and it will be right for the engine.
Link text is a signal about the destination. "Guide to accessibility" tells both a reader and a crawler what is at the other end; "click here" tells neither.
Language is declared on the root element, and on a multilingual site the versions are connected to each other in the head:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>HTML and SEO - Omni Academy</title>
<link rel="alternate" hreflang="en" href="https://example.com/courses/html/seo">
<link rel="alternate" hreflang="ar" href="https://example.com/ar/courses/html/seo">
<link rel="alternate" hreflang="x-default" href="https://example.com/courses/html/seo">
</head>
<body>
<h1>HTML and SEO</h1>
</body>
</html>
Open this example in the editor and change it. Nothing you type is saved.
hreflang tells an engine which version to show which audience, and x-default names the fallback. The rule that catches people: the set has to be reciprocal - every version must list every other, including itself - or the whole group is ignored.
Three myths worth retiring
Meta keywords. Ignored by every major engine since about 2009. Harmless, useless.
Keyword density. Writing a phrase nine times does not help and reads badly, which does hurt.
Hidden text for crawlers. Text positioned off screen or coloured to match the background, intended to be indexed but not seen, is a policy violation. Note the distinction from a visually-hidden label, which exists to be read aloud rather than to be indexed - that is legitimate and a different thing entirely.
Practice task
Take a page and check six things: unique title, one h1, no skipped heading levels, an absolute self-referential canonical, alt on every informative image, and link text that makes sense out of context. Then add a BreadcrumbList block and validate it with a structured-data testing tool. The validator will tell you about a missing required property in seconds, which is faster than waiting for a search result that never improves.