<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)
- 1993年に発表された最初の HTML 仕様ドラフト(いわゆる HTML 1.0)。これも
<meta>
要素は存在しない。 - HTML+ (Hypertext markup format)
<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 にはこんな制限がありました。
また、当時の HTTP 仕様である HTTP/1.0 (RFC 1945)(tools.ietf.org
) でも charset
パラメーターが省略された場合のデフォルト値が ISO-8859-1 と定められていました[2]。
このような事情もあったので、(実際の使われ方はともかくとして) HTML 仕様的には文字エンコーディングの指定を例示するほどその重要性は高くなかったのかもしれません。これは1997年に勧告された HTML 3.2 でも同様です。
一方、国際化を目指した RFC 2070 (HTML2.x)(tools.ietf.org
) では文字エンコーディングに関する記述も多く見られ、 <meta http-equiv="Content-Type">
のコード例も示されています。そこでは当時のサーバー状況を示すものとしてこんな記述があります。
そして、 <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 では、 <meta>
要素による文字エンコーディング指定の基本的な概念は HTML 2.x を踏襲していますが、使用条件と挿入位置についてより明確化されています。
日本語訳はこちら。
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が発表された 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日版で「文書の最初の512バイト内に完全にシリアル化されなければならない」という記述が追加されました。これは2011年4月5日版で1024バイト内に緩和され、現在に至ります。
また、文字エンコーディングそのものについての言及もあり、当初は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日版で追従しています。
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ではその3つの優先度が書かれています。しかし charset
属性は HTML5 で廃止されたので、現在では「Content-Type
ヘッダーは <meta>
要素より優先する」ことだけ覚えておけば大丈夫でしょう。
また、 <meta>
要素による文字エンコーディング指定の必要性は、現在の HTML 仕様ではこんな記述となっています。
要するに、 <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 範囲内かそうでないかに関係なく、より具体的な挿入範囲が明記されています。
例えばこんな 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]、まあその辺も鑑みて事前に意識をすり合わせるくらいのことは必要かもしれません。
脚注
-
1.
恥ずかしながらHTML仕様クイズ(メタデータ編)(
quiz-maker.site
)の6問目を誤答してしまいました(※他を正答したとは言ってない)。 ↩ 戻る -
2.
既にこのとき他の文字セットを使用する HTTP サーバーは増えていたようで、 HTTP 仕様でも「(ISO-8859-1 を前提とすることは)相互運用性を低下させるため勧められない」という旨の注釈(
tools.ietf.org
)が書かれています。しかし、次バージョンである HTTP/1.1 の初版(RFC 2068 (1997年)(tools.ietf.org
))と1回目の改訂版(RFC 2616 (1999年)(tools.ietf.org
))でもデフォルト値の規定は継続され、RFC 7231 (2014年)(tools.ietf.org
)でようやく撤回(tools.ietf.org
)されました。当然ながら、2014年の時点でcharset
パラメーター省略時に ISO-8859-1 を勝手にセットするサーバーは皆無であり、仕様の記述はとっくに形骸化していました。 ↩ 戻る - 3.
-
4.
例えば、テキストブラウザで有名な Lynx(
lynx.invisible-island.net
) は HTML5 普及後も長らく<meta charset>
に対応しておらず、2012年2月12日リリースの 2.8.8dev.10 で対応しました。その後も Lynx for Win32(lynx-win32-pata.osdn.jp
)は未対応の状況が続き、本家に追従した 2.8.9rel.16TH をリリースしたのは2017年9月9日です。 ↩ 戻る - 5.
-
6.
参考: HTML文書は文字エンコーディングUTF-8でなければなりません - 水底の血(
momdo.hatenablog.jp
) ↩ 戻る -
7.
XML 文書の場合は XML 宣言で良いケースもあります。 ↩ 戻る
- 8.
-
9.
もちろん紳士的に理由を説明させていただきましたよ! ↩ 戻る