---
title: "Technical SEO"
slug: "technical"
category: "seo"
tags: ["seo", "technical", "performance", "crawlability"]
status: "stable"
last_updated: 2026-05-14
summary: "Crawlability, canonicals, URL hygiene, Core Web Vitals, and rendering choices that decide whether a page can rank at all."
related: ["[[seo/content]]", "[[seo/structured-data]]", "[[seo/og-images]]", "[[seo/indexnow]]", "[[seo/crawl-budget]]", "[[seo/page-speed]]", "[[frontend/html]]", "[[seo/glossary]]", "[[seo/audit-checklist]]"]
---

## Overview

Technical SEO is the floor under everything else. A page can have perfect content and zero traffic if crawlers cannot reach it, browsers cannot render it fast, or the canonical points somewhere else. Treat this page as the pre-publish checklist for any site that wants organic search to work.

## Serve a clean robots.txt and a real sitemap.xml

Place both at the site root. `robots.txt` declares crawl scope; `sitemap.xml` declares what exists.

```
# https://example.com/robots.txt
User-agent: *
Allow: /
Disallow: /admin/
Disallow: /api/

Sitemap: https://example.com/sitemap.xml
```

The sitemap lists every canonical URL with `<lastmod>` from the page's `last_updated`. Regenerate it on build; never hand-edit. Submit it once in Google Search Console and Bing Webmaster Tools; the engines refetch on their own. Pair the sitemap with [[seo/indexnow]] for near-instant change notification on Bing and Yandex.

## One canonical per page, and the canonical is self-referential

Every indexable page declares its own URL as canonical:

```html
<link rel="canonical" href="https://example.com/seo/technical" />
```

Self-referential canonicals prevent parameter and protocol drift from splitting ranking signals. Non-canonical variants (UTM params, sorted lists, paginated tails) point at the canonical, not at themselves. Never declare two canonicals; never leave the tag missing on indexable pages.

## Pick one URL form and enforce it

URL drift fragments rankings. Pick one form per page and 301 every other form to it.

- One protocol: HTTPS only. 301 every `http://` request to `https://`.
- One host: `example.com` or `www.example.com`, not both.
- One trailing-slash policy: trailing slash on directories, no slash on leaf pages, or vice versa. Whichever you pick, redirect the other.
- Strip tracking parameters at the edge or canonicalize them away. `?utm_source=` should never produce a separate index entry.

## Hit Core Web Vitals on the 75th percentile

Google's field thresholds, measured on real users:

- [[glossary/lcp|LCP]] (Largest Contentful Paint) under 2.5 seconds.
- [[glossary/inp|INP]] (Interaction to Next Paint) under 200 milliseconds.
- [[glossary/cls|CLS]] (Cumulative Layout Shift) under 0.1.

Optimize in this order: ship less JavaScript, preload the LCP image with `fetchpriority="high"`, reserve space for images and embeds with `width` and `height`, defer third-party scripts. Verify with PageSpeed Insights against the CrUX field data, not lab scores. See [[seo/page-speed]] for the metrics Core Web Vitals does not cover, [[seo/image-seo]] for the image-side of the work, and [[howto/optimize-core-web-vitals]] for the end-to-end fix loop.

## One H1, headings in order, no skips

Each page has exactly one `<h1>`, and it matches the page intent. Subsections use `<h2>`; sub-cases use `<h3>`. Do not skip levels (no `<h2>` followed by `<h4>`). Do not style a `<div>` to look like a heading; screen readers and crawlers both read the DOM.

## SSR or static for content-heavy pages

Crawlers render JavaScript, but they render it on a budget and with delay. Google indexes the mobile version of every page (see [[seo/mobile-first]]), so the initial HTML response must be complete on a mid-range phone too. For any page that depends on search traffic, ship the content in the initial HTML response.

- Static generation (Astro, Next.js `output: "export"`, Quartz) for content that changes on deploy.
- SSR (Next.js [[frontend/nextjs-app-router|App Router]], Remix, Astro `output: "server"`) for content that changes per request but must be in the HTML.
- Client-only rendering only for authenticated, interactive surfaces that do not need to rank.

Pair the rendering choice with [[seo/structured-data]] in the same HTML response so the markup and the JSON-LD stay in sync.

## Related

- [[seo/content]]
- [[seo/structured-data]]
- [[seo/og-images]]
- [[seo/indexnow]]
- [[seo/llms-txt]]
- [[frontend/html]]
- [[seo/glossary]]
- [[seo/audit-checklist]]
