<meta charset="UTF-8"> を書く必要性があるケースとデメリット

投稿

HTML 文書内に <meta charset="UTF-8"> を書いていますか? 書いているとしたら、その必要性を問われた時に理由を説明できますか?

実は私も勘違いしていた部分があり[1]、改めてまとめてみました。

<meta> による文字エンコーディング指定の歴史

まず基本的なおさらいをします。<meta charset="UTF-8"> は HTML5 で登場した新しい記法で、 HTML4 以前は <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> などという長くて覚えにくい書き方をしていました。さらに遡ると、黎明期の HTML には meta 要素そのものが存在しません。

HTML4 より前の HTML

HTML が考案された当初、 meta 要素はありませんでした。

home of the first website(info.cern.ch)世界最初の Web ページ。ソースコード内に <meta> 要素は書かれていない。
Tags used in HTML(info.cern.ch)1992年(?)に書かれた HTML タグのメモ。 <meta> 要素は存在しない。
Hypertext Markup Language (HTML)(www.w3.org)1993年に発表された最初の HTML 仕様ドラフト(いわゆる HTML 1.0)。これも <meta> 要素は存在しない。
HTML+ (Hypertext markup format)(www.w3.org)<table> や <form> の追加が検討された文書。やはり <meta> 要素は存在しない。

meta 要素は1995年の HTML 2.0 (RFC 1866)(tools.ietf.org) で追加されました。ここで構文としては後にお馴染みとなる <meta http-equiv="" content=""> の書き方ができるようになったわけですが、仕様書の meta 要素の節(5.2.5)(tools.ietf.org)では Content-Type の例示はありません。

当時の HTML にはこんな制限がありました。

A document is a conforming HTML document if:

(中略)

* Its document character set includes [ISO-8859-1] and agrees with [ISO-10646]; that is, each code position listed in 13, "The HTML Coded Character Set" is included, and each code position in the document character set is mapped to the same character as [ISO-10646] designates for that code position.

1.2.1. Documents(tools.ietf.org)

また、当時の HTTP 仕様である HTTP/1.0 (RFC 1945)(tools.ietf.org) でも charset パラメーターが省略された場合のデフォルト値が ISO-8859-1 と定められていました[2]

このような事情もあったので、(実際の使われ方はともかくとして) HTML 仕様的には文字エンコーディングの指定を例示するほどその重要性は高くなかったのかもしれません。これは1997年に勧告された HTML 3.2(www.w3.org) でも同様です。

一方、国際化を目指した RFC 2070 (HTML2.x)(tools.ietf.org) では文字エンコーディングに関する記述も多く見られ、 <meta http-equiv="Content-Type"> のコード例も示されています。そこでは当時のサーバー状況を示すものとしてこんな記述があります。

Current HTTP servers, however, do not generally include an appropriate charset parameter with the Content-Type header.

6. External character encoding issues(tools.ietf.org)

そして、 <a> 要素と <link> 要素の charset 属性[3]および <meta http-equiv="Content-Type"> の指定は「Content-Type ヘッダーの charset パラメータ不在時の悪影響を抑えるための予防措置(preventive measures)」であるとされています。

また、1998年10月に富士ゼロックスの「XML Cafe」で公開されたcharsetパラメタの勧め: HTMLにおける文字符号化スキームの明示方法(web.archive.org)によると、 Apache は当時既に .htaccess などで charset パラメーターの指定が可能だった一方、「これが正しく行えないWWWサーバも存在します」と書かれています。 <meta> 要素が登場した当初の、'90年代後半の状況はそんなものだったのでしょう。

HTML4

1997年に勧告された HTML4 、および1999年に改定された HTML 4.01(www.w3.org) では、 <meta> 要素による文字エンコーディング指定の基本的な概念は HTML 2.x を踏襲していますが、使用条件と挿入位置についてより明確化されています。

The META declaration must only be used when the character encoding is organized such that ASCII-valued bytes stand for ASCII characters (at least until the META element is parsed). META declarations should appear as early as possible in the HEAD element.

5.2.2 Specifying the character encoding(www.w3.org)

日本語訳はこちら。

META宣言は、少なくともそのMETA要素がパースされるまでの間はASCIIの範囲内のバイト値がASCIIと同じ文字を表すような文字符号化方法が使われている場合にのみ用いることができる。META宣言は、 HEAD要素の、可能な限り先頭の方に現れねばならない。

5.2.2 文字符号化方法の指定(www.asahi-net.or.jp)

HTML 2.x でも挿入位置に関する記述はあったものの、「<head> 要素内のできるだけ先頭に含めることができる」くらいのニュアンスだったものが、 HTML4 では「可能な限り先頭の方に現れねばならない」とより強い表現になっています。

また、 <meta> 要素による文字エンコーディング指定より前に ASCII 範囲外の文字を含めることが禁止されました。これが何を意味するかというと「とりあえず HTML には <meta> 要素で文字エンコーディングを指定しておけばいいんでしょ」が通じないことを意味します。 Content-Type ヘッダーで charset パラメーターを設定している前提において、

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<!-- ここに日本語を入れる -->
<html lang="ja">
<head>
<title>サンプルページ</title>
</head>
<body>
<h1>サンプルページ</h1>
</body>
</html>

は可ですが、

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<!-- ここに日本語を入れる -->
<html lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>サンプルページ</title>
</head>
<body>
<h1>サンプルページ</h1>
</body>
</html>

はダメです。ダメと言っても DTD に照らし合わせて invalid になるわけではないですが、「仕様的には良くない HTML」という判断になると思います。

HTML5, HTML Living Standard

2008年に最初の Working Draft(www.w3.org)が発表された HTML5 では <meta charset="UTF-8"> という書き方ができるようになりました。突然 charset 属性なるものが登場したかと思えば、この記法は後方互換性をさほど気にすることなく使えるという触れ込みに当時不思議な感覚を抱いたものですが(新しい属性は古いブラウザでは認識されないことが常なので)、これはどうやら従来の <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> の記法が不正確に書かれた際のブラウザの挙動を仕様に逆輸入した結果によるものだったようで、その詳細は The HTML parser - Idiosyncrasies of the HTML parser(htmlparser.info) で解説されています。

HTML5 では従来の記法も認められていたものの、この簡便な記法は大抵の古いブラウザでも認識することからあっという間に広まったのですが、一方で厳格なパースを行う一部のマイナーなブラウザでは charset 属性を認識しないものもあり[4]、 Content-Type ヘッダーで charset パラメーターを指定しないままに <meta charset="UTF-8"> の新しい記法を導入したサイトで文字化けが発生する(そしてそのような環境はマイナー故に不具合が起こっていることが認知すらされない)という困った状況も起こりました[5]

<meta> 要素の挿入位置については2009年2月12日版(www.w3.org)で「文書の最初の512バイト内に完全にシリアル化されなければならない」という記述が追加されました。これは2011年4月5日版(www.w3.org)で1024バイト内に緩和され、現在に至ります(html.spec.whatwg.org)

また、文字エンコーディングそのものについての言及もあり、当初はAuthors are encouraged to use UTF-8.と UTF-8 が推奨されていました。しかしながら、2008年当時は iモードブラウザ1.0 など UTF-8 を表示できないブラウザも存在したので、少なくとも日本の Web サイトにおいては Shift_JIS 等が使われるケースもまだ多かったと記憶しています。

2012年に HTML 5.1 の Working Draft が発表された後、2013年5月28日版ではAuthors should use UTF-8.と表現が強くなっています。 HTML5 (HTML 5.0) も2013年8月6日版でその表現に追従しています。

さらに2017年には Living Standard において UTF-8 以外が禁止されるようになりました[6]。 W3C 側では(勧告まで進むことはありませんでしたが) HTML 5.3 の2018年4月26日版(www.w3.org)で追従しています。

Content-Type ヘッダーと <meta> の関係性と優先度

基本的な考え方は HTML 2.x 時代から変わらず、 Content-Type ヘッダーの charset パラメーターで文字エンコーディングが指定されていれば、 <meta> 要素による指定は必要ありません。両方が指定された場合、 Content-Type ヘッダーの指定が優先されます。

過去には Content-Type ヘッダー、 <meta> 指定、 charset 属性の3パターンの優先度を考慮すべき時代もありました。HTML 4.01 仕様の5.2.2 Specifying the character encoding(www.w3.org)ではその3つの優先度が書かれています。しかし charset 属性は HTML5 で廃止されたので、現在では「Content-Type ヘッダーは <meta> 要素より優先する」ことだけ覚えておけば大丈夫でしょう。

また、 <meta> 要素による文字エンコーディング指定の必要性は、現在の HTML 仕様ではこんな記述となっています。

If an HTML document does not start with a BOM, and its encoding is not explicitly given by Content-Type metadata, and the document is not an iframe srcdoc document, then the encoding must be specified using a meta element with a charset attribute or a meta element with an http-equiv attribute in the Encoding declaration state.

4.2.5.4 Specifying the document's character encoding(html.spec.whatwg.org)

HTML文書がBOMで開始せず、かつそのエンコーディングがContent-Typeメタデータによって明示的に指定されず、かつ文書がiframe srcdoc文書でない場合、エンコーディングはcharset属性をもつmeta要素、またはエンコーディング宣言状態のhttp-equiv属性をもつmeta要素を使用して指定されなければならない。

4.2.5.4 文書の文字エンコーディングを指定する(momdo.github.io)

要するに、 <meta> 要素による文字エンコーディング指定が必須なのは限定的な場面に限られるということです。

現実問題として、 HTML2.x や HTML 4.01 の時代はサーバー側の設定で Content-Type ヘッダーの charset パラメーターが付けられないケースも多かったため、やむなく HTML 側で文字エンコーディングを指定することもありましたが、現在ではそのようなケースも少なくなっています。

<meta> が必要なケース

Content-Type ヘッダーで charset パラメーターが指定されていない場合は HTML 仕様にも書かれているとおり、 <meta> 要素で文字エンコーディングを指定すべきです[7]

構文について、仕様上は <meta charset="UTF-8"> でも良いですが、前述のとおり一部のマイナーなブラウザでは最近まで charset 属性を認識しない事象がありましたし、もしかしたら今でも未対応の環境が残っているかもしれません。昔ながらの <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> の構文を採用した方が安全でしょう。また EPUB で電子書籍を作る場合、プラットフォーム側で旧構文が指示されるケースもあります[8]

なお、(電子書籍などではなく)ブラウザで閲覧される想定の Web ページの場合は、まずはサーバー側でレスポンスヘッダーの charset パラメーターを指定できないか検討すべきでしょう。過去にはMS10-090(KB2416400)を適用すると一部サイトがIEで文字化け(2010年12月)という事象もありました。もちろんこれはブラウザのバグであり、 Web ページ側が悪かったわけではありませんが、サーバー側でレスポンスヘッダーが指定されていれば防げました。原理的にはこのような事象が今後も発生する可能性はあります。

<meta> で文字エンコーディングを指定するデメリット

Content-Type ヘッダーで charset パラメーターが指定されている場合は <meta> 要素での文字エンコーディングの指定は任意です。 HTML をファイルとしてダウンロードしてオフラインで閲覧されるケースまで想定するなら、指定するメリットはあるかもしれません。

一方でデメリットも存在します。

前述のとおり、 HTML4 では「<meta> 要素による文字エンコーディング指定より前に非 ASCII 文字を含めることができない」という制限がありました。現在の HTML では ASCII 範囲内かそうでないかに関係なく、より具体的な挿入範囲が明記されています。

The element containing the character encoding declaration must be serialized completely within the first 1024 bytes of the document.

4.2.5.4 Specifying the document's character encoding(html.spec.whatwg.org)

文字エンコーディング宣言を含む要素は、文書の最初の1024バイト内に完全にシリアル化されなければならない。

4.2.5.4 文書の文字エンコーディングを指定する(momdo.github.io)

例えばこんな HTML があった場合。

<!DOCTYPE html>
<!-- (巨大なアスキーアートによるお遊び) -->
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Sample Page</title>
</head>
<body>
<h1>Sample Page</h1>
</body>
</html>

アスキーアートの文字量によって、この HTML は正当だったり不正になったりが変化します。アスキーアートを時々更新するサイトならば、運用ルールで「アスキーアートは○○バイト以内に収めなければならない」という縛りが必要になってしまいます。

あるいは JSP や PHP などサーバーサイドのテンプレートの書き方によっては、ページ先頭部分の条件処理などで、出力される HTML の先頭にインデントのスペースや改行が多く含まれることもあります。

⏎
␣␣⏎
␣␣␣␣⏎
␣␣␣␣⏎
␣␣␣␣⏎
␣␣⏎
␣␣⏎
␣␣␣␣⏎
␣␣␣␣⏎
␣␣⏎
⏎
⏎
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Sample Page</title>
</head>
<body>
<h1>Sample Page</h1>
</body>
</html>

これが極端になると、 <meta> 要素まで 1024 バイトを超えてしまうかもしれません。それではテンプレートを書くサーバーサイドの技術者に「最終的な出力でインデント等が○○バイト以内に収まるようにしなればならない」というルールを課せるでしょうか? いやいや、そんな要求をしたら怒られてしまいますよね。

いずれも、 <meta> 要素を書かなければそんな制限は不要です。

実際のところ Content-Type ヘッダーで charset パラメーターが指定されている限り、この 1024 バイト制限を守らなかったところでブラウザが文字化けを起こす可能性はないと思いますが、 HTML として正しくない状態であることに変わりはありませんし、文法的には問題なかった HTML4 時代と異なり、チェッカー(バリデーター)はエラーを吐きます。チェッカーがエラーを吐くということは、他の(本当に危ない)エラーが埋もれて見過ごされる可能性が高まるなど、実用上問題が起こりうる遠因にもなります。

このようなデメリットもあるので、 <meta> 要素による文字エンコーディング指定は何も考えずコピペで挿入するのではなく、メリットとデメリットを天秤に掛けたうえで慎重に判断されるべきでしょう。

ちなみに、あまりにも <meta> 要素の挿入が当たり前になってしまったが故に、まるで <meta> 要素が必須であるような事実誤認も一部には見られるようです。実際、私は( .htaccess ファイルで charset パラメーターの設定を書いたうえで) <meta> 要素なしの HTML を納品した際、客先から「<meta> も書けないなんてお前は素人か」といったクレームを頂いてしまったこともあるので[9]、まあその辺も鑑みて事前に意識をすり合わせるくらいのことは必要かもしれません。