# Limited render-blocking resources in head

- **Pattern:** `ab-002525` (`site-health-check.performance-signals.minimal-blocking`)
- **Severity:** info
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-002525
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-002525)

## Why it matters

Synchronous scripts in `<head>` block the HTML parser — the browser stops building the DOM and waits for each script to download, parse, and execute before continuing. On slow connections, 8+ blocking scripts can add multiple seconds to Time to First Paint. ISO 25010 performance-efficiency identifies render-blocking as a direct impairment to perceived responsiveness. The fix is attribute-level: adding `defer` or `async` costs nothing and eliminates the blocking behavior. Scripts that lack these attributes are typically there by accident — legacy integrations, copy-pasted snippets, or tools that don't set them by default.

## Severity rationale

Info because the actual impact depends on script count and sizes — a single small inline script is negligible, but this rating reflects the pattern rather than guaranteed high cost.

## Remediation

Add `defer` to external scripts in `<head>` that are not critical to initial render. Use `async` for independent scripts like analytics that do not depend on other scripts:

```html
<!-- Blocks rendering (bad) -->
<script src="/js/app.js"></script>

<!-- Deferred — runs after parsing, preserves order (good) -->
<script src="/js/app.js" defer></script>

<!-- Async — runs when ready, no order guarantee (good for analytics) -->
<script src="https://analytics.example.com/a.js" async></script>
```

`type="module"` scripts are deferred by default. For Next.js, the `<Script>` component's `strategy` prop controls loading: use `strategy="lazyOnload"` or `strategy="afterInteractive"` for non-critical scripts. Move scripts to just before `</body>` as a fallback if attribute changes are not feasible.

## Detection

- **ID:** `minimal-blocking`
- **Severity:** `info`
- **What to look for:** Count all `<script>` tags within the `<head>` section of the HTML. For each script, check whether it has a `defer`, `async`, or `type="module"` attribute. Count the number of synchronous scripts (those lacking all 3 of these non-blocking attributes). Inline scripts (no `src`) with small content under 1 KB are less impactful but still count.
- **Pass criteria:** Fewer than 8 synchronous (non-deferred, non-async, non-module) `<script>` tags are present in the `<head>`. Report the exact count: "Found X synchronous scripts in <head> (threshold: fewer than 8)."
- **Fail criteria:** 8 or more synchronous `<script>` tags in `<head>` that lack `defer`, `async`, and `type="module"` attributes.
- **Skip (N/A) when:** The `<head>` section contains 0 `<script>` tags (nothing to evaluate for render-blocking).
- **Error when:** SPA detected.
- **Detail on fail:** `"Found 12 synchronous script tags in <head> — these block page rendering"`
- **Remediation:** Synchronous scripts in the `<head>` block HTML parsing — the browser stops rendering until each script downloads and executes. Add `defer` or `async` to external scripts:

  ```html
  <!-- Before (blocking) -->
  <script src="analytics.js"></script>

  <!-- After (non-blocking) -->
  <script src="analytics.js" defer></script>
  ```

  `defer` preserves execution order and runs after parsing. `async` runs as soon as downloaded (use for independent scripts like analytics). For modern code, `type="module"` is deferred by default.

---

## External references

- iso-25010 performance-efficiency.time-behaviour

Taxons: performance

HTML version: https://auditbuffet.com/patterns/ab-002525
