6.

<head> の内容が <body> に移動する原因と対処(HTML5 パーサのエラー復旧)

編集
この記事の要点
  • <head> 内容が <body> に押し出される=HTML5 パーサのエラー復旧が動いた証拠
  • 原因 No.1: <head> 内に許可されない要素<div> / <p> / テキスト / 不正タグ)
  • 原因 No.2: BOM や謎の空白行<!DOCTYPE> より前にある
  • 原因 No.3: JSP / Blade / Twig の出力が テンプレタグの位置ミスで head 外へ流れた
  • 対処: W3C Markup Validator でチェック、ブラウザの View Source vs DevTools 差を確認
  • <head> に置けるのは <meta> / <title> / <link> / <script> / <style> / <base> / <noscript> / <template> のみ

現象

HTML ソースでは <head> 内に書いたはずの <meta> / <link> / <script> が、ブラウザの DevTools で見ると <body> 内に移動している現象。Chrome / Firefox / Safari 共通で起きます。

<!-- 書いたソース -->
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>My Page</title>
  <div>これが原因!</div>            <!-- ← head に div は置けない -->
  <link rel="stylesheet" href="style.css">
  <script src="app.js"></script>
</head>
<body>
  <h1>Hello</h1>
</body>
</html>
<!-- ブラウザが実際に解釈した DOM (Chrome DevTools で見える) -->
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>My Page</title>
  <!-- ← link と script が消えた -->
</head>
<body>
  <div>これが原因!</div>            <!-- ← head にあった div が body 先頭へ -->
  <link rel="stylesheet" href="style.css">  <!-- ← 一緒に押し出された -->
  <script src="app.js"></script>
  <h1>Hello</h1>
</body>
</html>

原因: HTML5 パーサのエラー復旧

HTML5 仕様(WHATWG HTML Living Standard)はパースエラー時の挙動を厳密に決めており、<head> 内に不正な要素が現れた瞬間に、その要素と以降の要素はすべて <body> へ移動するルールです。これにより「壊れた HTML でも何かは表示される」性質を担保しています。

<head> に置けるタグ一覧

タグ用途
<title>タブのタイトル(必須
<meta>charset / viewport / description / og:* 等
<link>CSS / favicon / preload / canonical
<script>外部 / インライン JS
<style>インライン CSS
<base>相対 URL の基底
<noscript>JS 無効時表示(head 内は <link>/<style>/<meta> 限定)
<template>クライアントテンプレ(head/body 両方可)

これ以外(<div> / <p> / <span> / 素のテキスト)を置くと押し出されます。

原因チェックリスト

① タグの誤配置

<!-- NG パターン -->
<head>
  <title>Page</title>
  Hello                             <!-- ← 素のテキスト NG -->
  <div class="header">...</div>     <!-- ← div NG -->
  <p>説明</p>                       <!-- ← p NG -->
  <h1>Title</h1>                    <!-- ← h1 NG -->
  <header>...</header>              <!-- ← header NG (HTML5) -->
</head>

② BOM や空白行

NG: ファイル先頭に BOM (EF BB BF) や空白行がある
   ↓
   <!DOCTYPE html>     ← BOM があると quirks mode に落ちる
   <html>

対処:
  - エディタで「BOM なし UTF-8」で保存し直す
  - VS Code: 右下のステータスバーで「UTF-8 with BOM」→「UTF-8」に変更
  - PHP: <?php の前に空白や改行を入れない(include 元も同様)

③ サーバ側テンプレートの出力ミス

<?php
// Laravel Blade / 素 PHP で起きやすい例

// NG: <?php タグの直前に空白
   <?php $title = 'My Page'; ?>

// NG: include されたファイルの末尾に空白行があり、それが <head> 内に流れ込む

// NG: BOM 付きファイル
// → output buffer 取って一括出力するか、文字コード変換ツールで一括解消

Laravel Blade の場合:

{{-- NG: @section の外にテキストや HTML を書いている (head に流れ込む可能性) --}}
@extends('layout')

これは body 用テキスト       {{-- ← レイアウト次第で <head> 内に出る --}}

@section('content')
  <h1>Hello</h1>
@endsection

{{-- OK: 必ず @section 内で出力 --}}
@extends('layout')

@section('content')
  <h1>Hello</h1>
@endsection

④ <meta charset> の位置

<meta charset="UTF-8"><head> 開始から 1024 バイト以内に置く必要があります。先に長いコメントや別 meta があると charset が無視され、エラー復旧が走ることがあります。

<!-- OK -->
<head>
  <meta charset="UTF-8">              <!-- 最初に -->
  <meta name="viewport" content="...">
  <title>...</title>
  ...
</head>

確認手順

  1. Chrome DevTools の Sources タブで生 HTML を確認(サーバが何を返したか)
  2. 同じく Elements タブで実 DOM を確認
  3. 2 つを diff すると押し出されたタグが特定可能
  4. 生 HTML の <head> 内に置けないタグが無いか目視
  5. W3C Markup Validator に通す
# CLI で W3C 系チェック
npm install -g html-validate
html-validate index.html

# あるいは tidy
sudo apt install tidy
tidy -errors -quiet index.html

対処パターン

原因対処
head 内に div / p / 素テキスト削除 or body 側へ移動
BOM 付きファイル「BOM なし UTF-8」で保存
テンプレ include の末尾改行末尾の ?> を省略、ob_start 利用
meta charset が遅いhead 開始直後に置く
独自タグ(カスタム要素)HTML5 でカスタム要素は body 推奨。head に置くとパースエラー
コメントタグの誤閉じ<!-- ... --> 構造を再確認

FAQ

Q: head に Google Analytics の <script> を置きたいだけなのに body に行く
A: <script> 自体は head 可。直前に不正タグがあって連鎖押し出しされている。直前を確認。

Q: jQuery プラグインが <link> を動的に head に追加してくれない
A: document.head.appendChild(...) なら確実。jQuery $('head').append(...) はパース後の DOM 操作なので問題なし(パース時の押し出しと別の話)。

Q: View Source(生 HTML)と DevTools で差があるのが普通?
A: 動的 DOM 操作(JS)の影響もあるが、JS なしで読み込み直後に差がある場合はパーサ復旧。View Source は壊れた HTML、Elements は復旧後の DOM を表示。

編集
Post Share
子ページ

子ページはありません

同階層のページ
  1. 環境構築
  2. 文法
  3. 要素一覧
  4. API
  5. テンプレートエンジン
  6. <head>タグに書いた内容が<body>タグに移動する場合の解決方法
  7. 空白(スペース)の入力方法
  8. テキスト/セレクトボックス/ラジオボタン変更時のJS関数呼び出し
  9. buttonでformをsubmitさせない方法

最近更新/作成されたページ