---
title: "What is Capo.js? Why HTML Head Tag Order Matters"
description: "How HTML head tag ordering affects page load performance. Complete Capo.js weight reference, common mistakes, and how Unhead automates optimal ordering."
canonical_url: "https://unhead.unjs.io/learn/guides/what-is-capo"
last_updated: "2026-03-05"
---

## What is Capo.js?

[Capo.js](https://github.com/nickvdh/capo.js) is a set of rules by [Rick Viscomi](https://rviscomi.dev/) (Chrome DevRel) defining the optimal order of HTML `<head>` tags for page load performance. Named after the guitar capo - it tunes your `<head>` by putting every tag in the right position.

Browsers parse `<head>` top-to-bottom. Wrong order means delayed rendering, unnecessary re-parsing, and slower LCP.

<callout icon="i-ph-rocket-launch-duotone">

**Unhead implements Capo.js sorting automatically.** Every tag from `useHead()` is placed in optimal position - zero config.

</callout>

## Why order matters

The HTML parser processes `<head>` sequentially:

- `<meta charset>` **after the title** → browser re-parses the document when it discovers a different encoding
- **Sync** `<script>` **before CSS** → blocks both rendering and stylesheet loading
- `<link rel="preconnect">` **after the resource it helps** → arrives too late to matter
- **Render-blocking resources before critical metadata** → delays the browser's understanding of the page

Bad order:

```html
<head>
  <title>My Page</title>
  <script src="/app.js"></script>
  <link rel="stylesheet" href="/style.css">
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <link rel="preconnect" href="https://fonts.googleapis.com">
</head>
```

Problems: charset after title triggers re-parse, sync script blocks CSS, preconnect comes after the stylesheet that needs it.

With Capo.js ordering:

```html
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>My Page</title>
  <link rel="preconnect" href="https://fonts.googleapis.com">
  <link rel="stylesheet" href="/style.css">
  <script src="/app.js"></script>
</head>
```

## Complete Weight Reference

Unhead implements all 14 Capo.js weight levels. Lower weight = placed first in `<head>`:

<table>
<thead>
  <tr>
    <th>
      Priority
    </th>
    
    <th>
      Weight
    </th>
    
    <th>
      Tag
    </th>
    
    <th>
      Why
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      1
    </td>
    
    <td>
      -30
    </td>
    
    <td>
      <code className="language-html shiki shiki-themes github-light github-light material-theme-palenight" language="html" style="">
        <span class="sx-uw">
          <
        </span>
        
        <span class="sV-QU">
          meta
        </span>
        
        <span class="sg-iE">
          http-equiv
        </span>
        
        <span class="sx-uw">
          =
        </span>
        
        <span class="sbw7o">
          "
        </span>
        
        <span class="sJnJ8">
          Content-Security-Policy
        </span>
        
        <span class="sbw7o">
          "
        </span>
        
        <span class="sx-uw">
          >
        </span>
      </code>
    </td>
    
    <td>
      <strong>
        The Preload Scanner Stall:
      </strong>
      
       Must be first. Late CSP tags can <a href="https://issues.chromium.org/issues/40273969" rel="nofollow">
        disable the Chromium preload scanner
      </a>
      
      , forcing resources to load sequentially.
    </td>
  </tr>
  
  <tr>
    <td>
      2
    </td>
    
    <td>
      -20
    </td>
    
    <td>
      <code className="language-html shiki shiki-themes github-light github-light material-theme-palenight" language="html" style="">
        <span class="sx-uw">
          <
        </span>
        
        <span class="sV-QU">
          meta
        </span>
        
        <span class="sg-iE">
          charset
        </span>
        
        <span class="sx-uw">
          >
        </span>
      </code>
    </td>
    
    <td>
      Encoding must be in first 1024 bytes
    </td>
  </tr>
  
  <tr>
    <td>
      3
    </td>
    
    <td>
      -15
    </td>
    
    <td>
      <code className="language-html shiki shiki-themes github-light github-light material-theme-palenight" language="html" style="">
        <span class="sx-uw">
          <
        </span>
        
        <span class="sV-QU">
          meta
        </span>
        
        <span class="sg-iE">
          name
        </span>
        
        <span class="sx-uw">
          =
        </span>
        
        <span class="sbw7o">
          "
        </span>
        
        <span class="sJnJ8">
          viewport
        </span>
        
        <span class="sbw7o">
          "
        </span>
        
        <span class="sx-uw">
          >
        </span>
      </code>
    </td>
    
    <td>
      Prevents mobile layout shifts
    </td>
  </tr>
  
  <tr>
    <td>
      4
    </td>
    
    <td>
      -10
    </td>
    
    <td>
      <code className="language-html shiki shiki-themes github-light github-light material-theme-palenight" language="html" style="">
        <span class="sx-uw">
          <
        </span>
        
        <span class="sV-QU">
          base
        </span>
        
        <span class="sx-uw">
          >
        </span>
      </code>
    </td>
    
    <td>
      Affects all relative URLs after it
    </td>
  </tr>
  
  <tr>
    <td>
      5
    </td>
    
    <td>
      10
    </td>
    
    <td>
      <code className="language-html shiki shiki-themes github-light github-light material-theme-palenight" language="html" style="">
        <span class="sx-uw">
          <
        </span>
        
        <span class="sV-QU">
          title
        </span>
        
        <span class="sx-uw">
          >
        </span>
      </code>
    </td>
    
    <td>
      First visible content in browser tab
    </td>
  </tr>
  
  <tr>
    <td>
      6
    </td>
    
    <td>
      20
    </td>
    
    <td>
      <code className="language-html shiki shiki-themes github-light github-light material-theme-palenight" language="html" style="">
        <span class="sx-uw">
          <
        </span>
        
        <span class="sV-QU">
          link
        </span>
        
        <span class="sg-iE">
          rel
        </span>
        
        <span class="sx-uw">
          =
        </span>
        
        <span class="sbw7o">
          "
        </span>
        
        <span class="sJnJ8">
          preconnect
        </span>
        
        <span class="sbw7o">
          "
        </span>
        
        <span class="sx-uw">
          >
        </span>
      </code>
    </td>
    
    <td>
      Early DNS + TLS for critical origins
    </td>
  </tr>
  
  <tr>
    <td>
      7
    </td>
    
    <td>
      30
    </td>
    
    <td>
      <code className="language-html shiki shiki-themes github-light github-light material-theme-palenight" language="html" style="">
        <span class="sx-uw">
          <
        </span>
        
        <span class="sV-QU">
          script
        </span>
        
        <span class="sg-iE">
          async
        </span>
        
        <span class="sx-uw">
          >
        </span>
      </code>
    </td>
    
    <td>
      Fetch ASAP, non-blocking
    </td>
  </tr>
  
  <tr>
    <td>
      8
    </td>
    
    <td>
      40
    </td>
    
    <td>
      <code className="language-html shiki shiki-themes github-light github-light material-theme-palenight" language="html" style="">
        <span class="sx-uw">
          <
        </span>
        
        <span class="sV-QU">
          style
        </span>
        
        <span class="sx-uw">
          >
        </span>
      </code>
      
       with <code className="language-css shiki shiki-themes github-light github-light material-theme-palenight" language="css" style="">
        <span class="smL2f">
          @import
        </span>
      </code>
    </td>
    
    <td>
      Render-blocking; must discover imports early
    </td>
  </tr>
  
  <tr>
    <td>
      9
    </td>
    
    <td>
      50
    </td>
    
    <td>
      <code className="language-html shiki shiki-themes github-light github-light material-theme-palenight" language="html" style="">
        <span class="sx-uw">
          <
        </span>
        
        <span class="sV-QU">
          script
        </span>
        
        <span class="sg-iE">
          src
        </span>
        
        <span class="sx-uw">
          >
        </span>
      </code>
      
       (sync)
    </td>
    
    <td>
      Render-blocking but unavoidable
    </td>
  </tr>
  
  <tr>
    <td>
      10
    </td>
    
    <td>
      60
    </td>
    
    <td>
      <code className="language-html shiki shiki-themes github-light github-light material-theme-palenight" language="html" style="">
        <span class="sx-uw">
          <
        </span>
        
        <span class="sV-QU">
          link
        </span>
        
        <span class="sg-iE">
          rel
        </span>
        
        <span class="sx-uw">
          =
        </span>
        
        <span class="sbw7o">
          "
        </span>
        
        <span class="sJnJ8">
          stylesheet
        </span>
        
        <span class="sbw7o">
          "
        </span>
        
        <span class="sx-uw">
          >
        </span>
      </code>
      
       / <code className="language-html shiki shiki-themes github-light github-light material-theme-palenight" language="html" style="">
        <span class="sx-uw">
          <
        </span>
        
        <span class="sV-QU">
          style
        </span>
        
        <span class="sx-uw">
          >
        </span>
      </code>
    </td>
    
    <td>
      Render-blocking CSS
    </td>
  </tr>
  
  <tr>
    <td>
      11
    </td>
    
    <td>
      70
    </td>
    
    <td>
      <code className="language-html shiki shiki-themes github-light github-light material-theme-palenight" language="html" style="">
        <span class="sx-uw">
          <
        </span>
        
        <span class="sV-QU">
          link
        </span>
        
        <span class="sg-iE">
          rel
        </span>
        
        <span class="sx-uw">
          =
        </span>
        
        <span class="sbw7o">
          "
        </span>
        
        <span class="sJnJ8">
          preload
        </span>
        
        <span class="sbw7o">
          "
        </span>
        
        <span class="sx-uw">
          >
        </span>
      </code>
      
       / <code className="language-html shiki shiki-themes github-light github-light material-theme-palenight" language="html" style="">
        <span class="sx-uw">
          <
        </span>
        
        <span class="sV-QU">
          link
        </span>
        
        <span class="sg-iE">
          rel
        </span>
        
        <span class="sx-uw">
          =
        </span>
        
        <span class="sbw7o">
          "
        </span>
        
        <span class="sJnJ8">
          modulepreload
        </span>
        
        <span class="sbw7o">
          "
        </span>
        
        <span class="sx-uw">
          >
        </span>
      </code>
    </td>
    
    <td>
      Hints for soon-needed resources
    </td>
  </tr>
  
  <tr>
    <td>
      12
    </td>
    
    <td>
      80
    </td>
    
    <td>
      <code className="language-html shiki shiki-themes github-light github-light material-theme-palenight" language="html" style="">
        <span class="sx-uw">
          <
        </span>
        
        <span class="sV-QU">
          script
        </span>
        
        <span class="sg-iE">
          defer
        </span>
        
        <span class="sx-uw">
          >
        </span>
      </code>
      
       / <code className="language-html shiki shiki-themes github-light github-light material-theme-palenight" language="html" style="">
        <span class="sx-uw">
          <
        </span>
        
        <span class="sV-QU">
          script
        </span>
        
        <span class="sg-iE">
          type
        </span>
        
        <span class="sx-uw">
          =
        </span>
        
        <span class="sbw7o">
          "
        </span>
        
        <span class="sJnJ8">
          module
        </span>
        
        <span class="sbw7o">
          "
        </span>
        
        <span class="sx-uw">
          >
        </span>
      </code>
    </td>
    
    <td>
      Non-blocking, post-parse execution
    </td>
  </tr>
  
  <tr>
    <td>
      13
    </td>
    
    <td>
      90
    </td>
    
    <td>
      <code className="language-html shiki shiki-themes github-light github-light material-theme-palenight" language="html" style="">
        <span class="sx-uw">
          <
        </span>
        
        <span class="sV-QU">
          link
        </span>
        
        <span class="sg-iE">
          rel
        </span>
        
        <span class="sx-uw">
          =
        </span>
        
        <span class="sbw7o">
          "
        </span>
        
        <span class="sJnJ8">
          prefetch
        </span>
        
        <span class="sbw7o">
          "
        </span>
        
        <span class="sx-uw">
          >
        </span>
      </code>
      
       / <code className="language-html shiki shiki-themes github-light github-light material-theme-palenight" language="html" style="">
        <span class="sx-uw">
          <
        </span>
        
        <span class="sV-QU">
          link
        </span>
        
        <span class="sg-iE">
          rel
        </span>
        
        <span class="sx-uw">
          =
        </span>
        
        <span class="sbw7o">
          "
        </span>
        
        <span class="sJnJ8">
          dns-prefetch
        </span>
        
        <span class="sbw7o">
          "
        </span>
        
        <span class="sx-uw">
          >
        </span>
      </code>
    </td>
    
    <td>
      Low-priority future navigation hints
    </td>
  </tr>
  
  <tr>
    <td>
      14
    </td>
    
    <td>
      100
    </td>
    
    <td>
      Everything else
    </td>
    
    <td>
      <code className="language-html shiki shiki-themes github-light github-light material-theme-palenight" language="html" style="">
        <span class="sx-uw">
          <
        </span>
        
        <span class="sV-QU">
          meta
        </span>
        
        <span class="sg-iE">
          name
        </span>
        
        <span class="sx-uw">
          =
        </span>
        
        <span class="sbw7o">
          "
        </span>
        
        <span class="sJnJ8">
          description
        </span>
        
        <span class="sbw7o">
          "
        </span>
        
        <span class="sx-uw">
          >
        </span>
      </code>
      
      , OG tags, JSON-LD
    </td>
  </tr>
</tbody>
</table>

### Weight groups

**-30 to -10 (critical metadata)** must appear first - `<meta charset>` after 1024 bytes forces re-parsing, late viewport causes mobile layout shifts.

**10-20 (content & connections):** `<title>` for the browser tab, `<link rel="preconnect">` to establish connections before they're needed.

**30-60 (render-blocking resources):** `<script async>` gets discovered early for fetching. Sync `<script>` and `<link rel="stylesheet">` in their optimal relative order.

**70-100 (deferred & hints):** `<link rel="preload">`, `<script defer>`, `<link rel="prefetch">`, and everything else. These don't affect initial rendering.

## Common Mistakes

### The "Head-Breaker"

A common mistake is placing an invalid tag (like `<img>` or `<iframe>`) inside the `<head>`. The browser's DOM builder implicitly closes the `<head>` and moves everything after the invalid tag to the `<body>`.

<chart-head-breaker-impact>



</chart-head-breaker-impact>

According to the **Web Almanac 2025**, invalid head markup remains a significant issue for **22% of all mobile pages**. This "breaks" SEO and performance because the parser discovers critical meta tags and styles too late.

### Charset after the title

```html
<!-- Bad -->
<title>My Page</title>
<meta charset="utf-8">

<!-- Good -->
<meta charset="utf-8">
<title>My Page</title>
```

The [HTML spec](https://html.spec.whatwg.org/multipage/semantics.html#charset) requires `<meta charset>` within the first 1024 bytes. Content before it may trigger re-parsing.

### CSS before preconnects

```html
<!-- Bad: preconnect arrives too late -->
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter">
<link rel="preconnect" href="https://fonts.googleapis.com">

<!-- Good -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter">
```

### Sync scripts blocking CSS

```html
<!-- Bad: script blocks CSS loading -->
<script src="/analytics.js"></script>
<link rel="stylesheet" href="/style.css">

<!-- Good -->
<link rel="stylesheet" href="/style.css">
<script src="/analytics.js"></script>
```

Better - make analytics async:

```html
<script async src="/analytics.js"></script>
<link rel="stylesheet" href="/style.css">
```

### Preloads after what they preload

```html
<!-- Bad: preload discovered after stylesheet -->
<link rel="stylesheet" href="/style.css">
<link rel="preload" href="/font.woff2" as="font" crossorigin>

<!-- Good -->
<link rel="preload" href="/font.woff2" as="font" crossorigin>
<link rel="stylesheet" href="/style.css">
```

## Automatic Sorting with Unhead

Every `useHead()` call produces Capo.js-ordered output:

```ts
import { useHead } from 'unhead'

useHead({
  meta: [
    { charset: 'utf-8' }, // weight: -20
    { name: 'viewport', content: 'width=device-width' }, // weight: -15
    { name: 'description', content: 'My page' }, // weight: 100
  ],
  title: 'My Page', // weight: 10
  link: [
    { rel: 'preconnect', href: 'https://fonts.googleapis.com' }, // weight: 20
    { rel: 'stylesheet', href: '/style.css' }, // weight: 60
  ],
})
```

Unhead sorts the output by weight regardless of input order.

### Custom priority

Override the default ordering with `tagPriority`:

```ts
useHead({
  script: [
    {
      src: '/critical-polyfill.js',
      tagPriority: 'critical', // -8 offset (higher priority)
    },
    {
      src: '/analytics.js',
      tagPriority: 'low', // +2 offset (lower priority)
    },
  ],
})
```

Priority aliases (applied as offsets to the tag's calculated weight):

- `'critical'` - subtracts 8 from weight
- `'high'` - subtracts 1
- `'low'` - adds 2
- Any number - exact weight override

Relative positioning with `before:` and `after:` prefixes:

```ts
useHead({
  script: [
    {
      src: '/my-script.js',
      tagPriority: 'before:script:analytics',
    },
  ],
})
```

## Check Your Site

<callout icon="i-ph-magnifying-glass-duotone" title="Capo Analyzer" to="/tools/capo-analyzer">

Paste HTML or enter a URL to get instant feedback on your head tag ordering, with specific suggestions for improvement.

</callout>

For the measured performance impact of head tag ordering, including the **"Nuxt Paradox"** where automatic ordering isn't enough to fix slow LCP, see [Does Head Tag Order Affect Performance?](/learn/research/capo-performance-research). For how streaming SSR interacts with head management, see [Streaming SSR SEO](/learn/research/streaming-head-performance).
