HTML head 内タグの最新の書き方

HTML の <head> 内の一部のタグについて現行 HTML Living Standard ではどのように書くのが正しいのか、経緯も含め公式の仕様策定を引用しながら調べました。最近こういう書き方しないんだっけ?という人のお役に立てばと思います。

やりたいこと

普段 React や Vue.js を使用しているとプロジェクト作成時にエントリーポイントとなる HTML が自動生成されますし、HTML を自分で書くときも VSCode のスニペットを使ったりしていると HTML の <head> の中をまじまじと一から書くことがほとんどありません。最近 HTML をレビューする機会があって、あれ? HTML の <head> ってこんなんだっけか?と思うことがあり、一部ではありますがちゃんと調べてみました。様々なサイトで HTML5 ではこうだ、と言った記載はよく目にするのですが、人に説明するにあたって根拠もしっかり示したく、極力公式のドキュメントで調べました。

なお、SEO 用のタグの書き方については多くのサイトで紹介されていますので、ここで取り上げるのは 純粋に HTML を書く上で必要なタグにフォーカスします。

<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

まず目に飛び込んで来たのがこちらです。

<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

昔よく目にしましたが、最近は文字コードの指定のみの

<meta charset="utf-8">

としているよねと思い、まず MDN で調べます。

content-type 文書の MIME タイプを宣言するもので、後に文字エンコーディングの定義が続きます。指定された場合、 content 属性には "text/html; charset=utf-8" 設定しなければなりません。これは <meta> 要素に charset 属性を指定した場合と同じであり、文書内の位置の制約も同様になります。 - <meta>: メタデータ要素 - HTML: HyperText Markup Language | MDN

つまり、<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /><meta charset="utf-8"> と同義となります。 上記 MDN の表現は (元の英語版も含め) 両者は同等のような記載となっていますが、本家 HTML Living Standard では以下のように記載されています。

The Encoding declaration state is just an alternative form of setting the charset attribute: it is a character encoding declaration. - HTML Standard

あくまで個人的な解釈ですが、エンコーディング指定は <meta charset="utf-8"> が主であり、<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> はその代替というふうに読めるような気がします。 また、HTML Living Standard には以下の記載もあります。

A document must not contain both a meta element with an http-equiv attribute in the Encoding declaration state and a meta element with the charset attribute present. - HTML Standard

つまり <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /><meta charset="utf-8"> は共存できないとのことです。

まだちょっと気持ち悪いので HTML 仕様策定が W3C 政権だった頃のドキュメント HTML5 Differences from HTML4 を掘り出してみます。すると以下の記載がありました。

Using a meta element with a charset attribute that specifies the encoding within the first 1024 bytes of the document; for instance, <meta charset="UTF-8"> could be used to specify the UTF-8 encoding. This replaces the need for <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> although that syntax is still allowed. - HTML5 Differences from HTML4

これを読むと、<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /><meta charset="utf-8"> の後方互換の位置づけであるように見えます。

これで少しすっきりしたので、やはり現行 HTML では <meta charset="utf-8"> で指定するのが良さそうです。

<meta http-equiv="Content-Style-Type" content="text/css" />

次に目に入ったのがこちら。

<meta http-equiv="Content-Style-Type" content="text/css" />

遠い昔に見たことあるような気がしなくもないのですが、出かかってもこないレベルの記憶です。

HTML Living Standard を覗いてみると以下の記述が。

The http-equiv attribute is an enumerated attribute. - HTML Standard

http-equiv 属性の値は列挙型 (所謂 enum) で、その値は以下の通りです。

  • content-language
  • content-type
  • default-style
  • refresh
  • set-cookie
  • x-ua-compatible
  • content-security-policy

したがって少なくとも現行 HTML においては <meta http-equiv="Content-Style-Type" content="text/css" /> は無効のようです。

またまた気持ち悪いので、W3C の HTML 4.01 Specification を見てみます。

The http-equiv attribute can be used in place of the name attribute and has a special significance when documents are retrieved via the Hypertext Transfer Protocol (HTTP). HTTP servers may use the property name specified by the http-equiv attribute to create an [RFC822]-style header in the HTTP response. Please see the HTTP specification ([RFC2616]) for details on valid HTTP headers. - The global structure of an HTML document

Web サーバーが http-equiv 属性の値を元に HTTP ヘッダーを返してくれるようです。ただ、RFC2616Content-Style-Type なんていうヘッダーは出てきません (HTML4 時代からそもそもいらなかったのでは…)。

そしてまた先ほどの HTML5 Differences from HTML4 を見てみます。

The http-equiv attribute on meta is no longer said to be used by HTTP servers to create HTTP headers in the HTTP response. Instead, it is said to be a pragma directive to be used by the user agent. - HTML5 Differences from HTML4

Web サーバーが返す HTTP ヘッダーに使用することはしないとのこと。結論 <meta http-equiv="Content-Style-Type" content="text/css" /> は不要。

<meta http-equiv="Content-Script-Type" content="text/javascript" />

<meta http-equiv="Content-Style-Type" content="text/css" /> と同様。不要。

次はこちら。

<link rel="stylesheet" type="text/css" href="/css/style.css" />

外部スタイルシートの読み込みで <link> タグは使うのですが、最近 type 属性って見ないようなと思いました。

この属性は、リンク先コンテンツの種類を定義します。この属性の値は text/htmltext/css などの MIME タイプにします。 この属性の一般的な使用法は、参照されるスタイルシートのタイプ (text/css など) の定義ですが、 CSS はウェブ上の唯一のスタイルシート言語であるため、 type 属性を省略できるばかりでなく、それが実際に推奨される習慣になっています。 - <link>: 外部リソースへのリンク要素 - HTML: HyperText Markup Language | MDN

これが全てですね。一応 W3C の HTML 4.01 Specification を見てみると、

When the LINK element links an external style sheet to a document, the type attribute specifies the style sheet language and the media attribute specifies the intended rendering medium or media. - Links in HTML documents

とあり、HTML4 時代は必要だったようです。そして HTML5 Differences from HTML4 にはバッチリ以下の記載がありました。

The type attribute on script and style is no longer required if the scripting language is JavaScript and the styling language is CSS, respectively. - HTML5 Differences from HTML4

以上から外部スタイルシート読み込みにおける type 属性は不要です。

<script type="text/javascript" src="/js/script.js"></script>

<link rel="stylesheet" type="text/css" href="/css/style.css" /> と同様で type 属性は不要です。

MDN にも以下記載があります。

省略または JavaScript の MIME タイプ: これはスクリプトが JavaScript であることを示します。 HTML5 仕様書では、冗長な MIME タイプを指定せずに属性を省略するよう主張しています。 - <script>: スクリプト要素 - HTML: HyperText Markup Language | MDN

余談: 自己終了タグ

<meta> のような空要素を自己終了タグ (日本語で正しい言い方がわからない。Self-closing) として <meta /> のように記載されていることがありますが、これは XHTML の名残であり、HTML では不要です (誤りではないが何の意味も持たない)。

Then, if the element is one of the void elements, or if the element is a foreign element, then there may be a single U+002F SOLIDUS character (/), which on foreign elements marks the start tag as self-closing. On void elements, it does not mark the start tag as self-closing but instead is unnecessary and has no effect of any kind. - HTML Standard

また自己終了タグを使う際に属性値をクォーテーションで囲まない場合、属性値とスラッシュの間にスペースがないと属性値の一部として認識されるので注意が必要です。

For such void elements, it should be used only with caution — especially since, if directly preceded by an unquoted attribute value, it becomes part of the attribute value rather than being discarded by the parser. - HTML Standard

普段属性値をクォーテーションで囲まない書き方なんてまずしないので試してみました。

<body>
    <input type=text/>
    <script>
        const type = document.querySelector('input').getAttribute('type');
        console.log(type);
    </script>
</body>

コンソールには見事に text/ と出力されました。もちろん querySelector('input[type="text"]')<input> 要素を拾うこともできませんでした。 なお属性値とスラッシュの間にスペースを入れて <input type=text /> としてやれば大丈夫です。

あとがき

今更になって 一時期の HTML5 や HTML Living Standard を真面目に見たので、古いドキュメントを引っぱり出したりとなかなか大変でしたが、概ね公式の仕様を辿れてすっきりしました。