HTML Metadata
Nothing in the head renders. It still decides the browser tab, the bookmark, the search result, the preview card when the link is pasted into a chat, and whether the page is indexed at all. Per line written, it is the highest-leverage markup on a page.
Here is a complete head, with everything that earns its place:
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>HTML Metadata - Omni Academy</title>
<meta name="description"
content="What belongs in the head of an HTML document: encoding, viewport, title, description, robots, canonical and the social preview tags.">
<link rel="canonical" href="https://example.com/courses/html/metadata">
<meta name="theme-color" content="#11785f">
<link rel="icon" href="/favicon.svg" type="image/svg+xml">
<link rel="apple-touch-icon" href="/apple-touch-icon.png">
<meta property="og:type" content="article">
<meta property="og:title" content="HTML Metadata">
<meta property="og:description" content="What belongs in the head of an HTML document.">
<meta property="og:url" content="https://example.com/courses/html/metadata">
<meta property="og:image" content="https://example.com/og/metadata.png">
<meta name="twitter:card" content="summary_large_image">
</head>
Open this example in the editor and change it. Nothing you type is saved.
The two that change how the document is read
charset and viewport were covered in the document-structure lesson, and they come first for a reason: both change how everything after them is interpreted, so a browser needs them before it has read far.
charset must appear within roughly the first kilobyte. viewport must be present or no responsive CSS has any effect on a phone.
Title and description
The title is the tab, the bookmark, the window and the blue line of a search result. Rules that follow from that list:
- Unique per page. Twenty pages called "Omni Academy" are twenty indistinguishable tabs.
- Front-load the specific part. "HTML Metadata - Omni Academy", not the other way round, because a narrow tab shows the first few characters.
- Around 60 characters. Longer is not wrong, but search results truncate.
meta name="description" is the sentence that may appear under it. It is not a ranking factor and it is not guaranteed - search engines frequently write their own snippet from the page when it answers the query better. Write it as the one sentence you would use to persuade somebody to click, around 150 characters, and accept that it is a suggestion.
Canonical
<link rel="canonical" href="https://example.com/courses/html/metadata">
Open this example in the editor and change it. Nothing you type is saved.
The same content is often reachable at several addresses: with and without a trailing slash, with tracking parameters, through a print version. A canonical link names which one is the real one, so the ranking credit from all of them lands in one place rather than being split.
Two rules. It must be absolute - a relative canonical is a common and silent mistake. And it must be self-referential on the canonical page itself: every page should point at its own true address.
Robots
<meta name="robots" content="noindex, nofollow">
Open this example in the editor and change it. Nothing you type is saved.
This instructs crawlers not to index the page or follow its links. The useful directives are noindex, nofollow, noarchive, nosnippet, and max-snippet, max-image-preview and max-video-preview for controlling how much may be shown.
Worth being clear about what this is not: it is not access control. A crawler that ignores the directive indexes the page anyway, and anyone with the URL can read it. Pages that must not be read need a login.
There is an interaction that catches people: a page blocked in robots.txt cannot be crawled, so its noindex is never read, so it can still appear in results as a bare URL. To keep something out of an index, allow the crawl and use noindex.
Favicons and theme colour
<link rel="icon" href="/favicon.svg" type="image/svg+xml">
<link rel="icon" href="/favicon.ico" sizes="32x32">
<link rel="apple-touch-icon" href="/apple-touch-icon.png">
<meta name="theme-color" content="#11785f">
Open this example in the editor and change it. Nothing you type is saved.
An SVG favicon scales to every size from one file and can respond to dark mode with a media query inside the SVG. The .ico stays as a fallback for older browsers, and apple-touch-icon is the home-screen icon on iOS, which needs to be a 180-pixel PNG.
theme-color tints browser UI - the address bar on Android, the title bar of an installed web app. It accepts a media attribute, so a site can offer one colour for light mode and another for dark.
Social previews
Open Graph decides what a link looks like when it is pasted into a chat, a post or a message.
<meta property="og:type" content="article">
<meta property="og:title" content="HTML Metadata">
<meta property="og:description" content="What belongs in the head of an HTML document.">
<meta property="og:url" content="https://example.com/courses/html/metadata">
<meta property="og:image" content="https://example.com/og/metadata.png">
<meta property="og:image:alt" content="A diagram of the head of an HTML document">
<meta name="twitter:card" content="summary_large_image">
Open this example in the editor and change it. Nothing you type is saved.
Note property rather than name on the Open Graph tags - a scraper looking for property will not find name, and the tag is silently ignored. That single typo is the commonest cause of a preview that will not generate.
The image must be an absolute URL and publicly reachable, with no login. Around 1200 by 630 pixels is the safe size. og:image:alt describes it, because previews are read aloud too.
A detail that confuses people: scrapers cache aggressively. Fix a broken preview and the old one often persists for days - most platforms have a debugging tool that forces a refresh, and that is the only reliable way to see your change.
Structured data
Everything above describes the page to a human reading a preview. Structured data describes it to a machine, in a vocabulary search engines and answer engines understand.
<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",
"url": "https://example.com"
}
}
</script>
Open this example in the editor and change it. Nothing you type is saved.
JSON-LD in a script tag is the recommended form: it sits in one block rather than being scattered through the markup as attributes, so it can be generated and validated on its own.
Two things to be honest about. Structured data makes a page eligible for a rich result; it does not guarantee one, and no amount of markup forces a search engine to show one. And it must describe what is actually on the page - marking up a rating that does not appear is a policy violation, not a trick.
There is a lesson on SEO later that goes further into which types are worth the effort.
Hints the browser can act on early
Two link relationships in the head change the order the network does its work. They belong to the performance lesson, and they live in the head, so they are worth recognising here.
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="preload" href="/fonts/inter.woff2" as="font" type="font/woff2" crossorigin>
Open this example in the editor and change it. Nothing you type is saved.
preconnect opens the connection to another origin before anything needs it, which saves the DNS lookup and the handshake. preload fetches a resource the parser has not reached yet at high priority - typically a font, which is otherwise discovered only after the CSS has loaded.
Both reward restraint: preloading everything means prioritising nothing, and a preload whose as is wrong fetches the file twice.
Practice task
Give a page a unique title, a description, a self-referential canonical, a favicon and a full set of Open Graph tags. Then paste the URL into a messaging app and look at the preview. If nothing appears, check three things in order: is og:image absolute, is the image publicly reachable, and did you write property rather than name.