ByteScope

HTML ファイル形式

HyperText Markup Language。ウェブページの構造を記述する形式。

MIME タイプ
text/html
拡張子
.html, .htm
カテゴリ
テキスト・マークアップ

マジックナンバー(ファイル署名)

'<!doctype html' または '<html' で始まるプレーンテキスト。

HTML 形式について

HTML を定義しているのは文法ではなくパーサーです。標準は「合法な文書の集合を示して残りを拒否する」という書き方をしていません。あらゆる入力に対してブラウザが何をしなければならないかを、バイト単位で記述しています。壊れた入力も、途中で切れた入力も、そもそも HTML ですらない入力も含めてです。だから実務上「不正な HTML ファイル」というものは存在しませんし、まったく同じ壊れ方のマークアップから 2 つのブラウザが同一のツリーを組み上げますし、パースを始める前にツールが確認できるヘッダも長さもバージョンもトレーラーも、この形式には 1 つもありません。

ヘッダーの構造(バイト単位)

オフセット長さフィールド意味
03UTF-8 byte-order markEF BB BF。これがあるとブラウザはそこで探すのをやめます。標準は「BOM があればエンコーディングは確定」と定めていて、それは Content-Type ヘッダの charset も、文書中の meta 要素も上書きします。HTML において固定バイトオフセットで決まることの、数少ない 1 つです。
015DOCTYPE3C 21 44 4F 43 54 59 50 45 20 68 74 6D 6C 3E、今どきの doctype の 15 バイトです。大文字小文字は問いませんし、ブラウザの sniffing 規則が要求するのは最初の 14 バイト(HTML までの文字)と、その後ろに続く空白か閉じ山括弧だけです。
05html start tag3C 68 74 6D 6C に、空白か閉じ山括弧が続いたもの。doctype は必須ではないので、これがオフセット 0 に来ても完全に合法です。ただし doctype の無い文書は quirks モードでパースされ、数十年前のレイアウト挙動が戻ってきて、ボックスの寸法計算が設計時のものと合わなくなります。
0variableAny other recognised openersniffing の表は HEAD、SCRIPT、IFRAME、H1、DIV、FONT、TABLE、A、STYLE、TITLE、B、BODY、BR、P とコメントの開始にも一致します。いずれも先頭の空白は許され、名前の後ろに空白か閉じ山括弧が必要です。ページから切り出して保存した断片は、上 2 行のどちらでもなく、たいていこれらのどれかに一致します。
01024Encoding prescan windowフィールドではありませんが、頭に入れておく価値のある境界です。ブラウザは meta の charset を先頭 1024 バイトの中でしか探さないことになっています。長いライセンスのコメントやインライン化したスクリプトのせいでそれより後ろへ押し出された charset 宣言は、単に見てもらえず、ページは推測にフォールバックします。

似た形式との見分け方

紛らわしい形式見分け方
XML3C 3F 78 6D で始まる文書は XML であり、application/xhtml+xml として配信された XHTML は XML のパーサーへ行き、そこでは閉じ忘れたタグ 1 つが致命傷になります。まったく同じバイト列が text/html として配信されれば、黙って復旧されて普通に描画されます。違いはファイルの中にはまったくありません。サーバーが送ったヘッダの中にあります。同じディレクトリにある同じ文書が、まるで違う 2 つの振る舞いをし得るのはそのためです。
SVG単体の .svg は svg をルート要素に持つ XML で、XML のパーサーが処理します。同じマークアップを HTML 文書の中にインラインで書くと、HTML のパーサーが foreign content の規則で処理し、タグ名の大小文字は補正され、自己閉じ構文も特別扱いされます。要素のバイト列は同一であり得て、意味を決めるのはどちらのパーサーがそれを見るかです。
Plain text/Markdown確実なバイトの検査は存在しません。どのタグのパターンにも一致しなければ、先頭にバイナリが含まれていない限り sniffing のアルゴリズムはプレーンテキストにフォールバックします。だから README も、変数で始まるテンプレートも、メールの本文も、バイトレベルでは散文と区別が付きません。手掛かりは拡張子と Content-Type だけで、どちらも証拠ではなく主張です。
Failed downloadshex ビューアで HTML について知っておくと一番役立つのは、ダウンロードの失敗の正体がたいていこれだということです。開けない ZIP、読み込めない画像、リーダーに拒否される PDF。開いてオフセット 0 に 3C 21 44 4F 43 54 59 50 45 があれば、見ているのはログインページか、レート制限の通知か、欲しかったファイルの名前で保存された 404 です。

よくある壊れ方

  • doctype の前に何かがある。空白とコメントは無視されて何のコストもありませんが、実際のテキストが前にあると(サーバーサイド言語が出した警告、include したファイルから紛れ込んだ 1 行)、文書は quirks モードへ落ちます。それでもページは描画されます。常に描画されるからです。ただしボックスの寸法計算や継承されるフォントサイズが、検証したレイアウトと微妙に違います。
  • エンコーディングがどこにも宣言されていない。BOM も無く、Content-Type にも charset が無く、先頭 1024 バイトに meta も無ければ、ブラウザは推測しますし、その推測は読み手のロケールに左右されます。作った職場では正しく見えて、別のどこかでは文字化けだらけになります。バイトはどの時点でも変わっていないのに。
  • 途中切れ。中途半端なダウンロードには手掛かりがまったくありません。パーサーは入力が尽きた時点で開いている要素をすべて閉じ、手元にあるものを描画します。フッターの無いページは、途中で止まったファイルというより、テンプレートのバグに見えます。確かめる唯一の方法は、バイト数を告知された Content-Length と比べることです。
  • 二重エスケープ。テンプレートを通る途中で 2 回エスケープされたテキストは、書式ではなくタグの構文がそのまま見える形で描画されます。しかもその間ファイルは完全に整形式のままです。探すべきバイト列は 26 61 6D 70 3B、つまりエスケープされたアンパサンドが、エスケープされた山括弧の前に居座っている状態です。
  • 保存したページは、そのページではない。名前を付けて保存は URL を書き換え、リソースを移動またはインライン化し、出どころを記録するコメントを差し込みます。だからディスク上のバイトは、サーバーが送ったバイトではありません。保存したページと取り直したものを diff しても、報告されるのはほとんどブラウザ自身の編集なので、保存した控えは証拠としてほぼ役に立ちません。

この形式の現在地

HTML が置き換えられることはないので、面白い問いは「ブラウザの外のどこに出てくるか」のほうです。最大の場所はメールで、しかも化石のような部分集合(テーブルレイアウトとインラインスタイル)で動いています。メールクライアントはパースの標準を採用しませんでしたし、これからもしません。Electron とその模倣品はデスクトップアプリの中にブラウザエンジンを入れたので、人が毎日使うソフトウェアのかなりの割合は、表面が HTML です。静的サイトジェネレータ、ドキュメントツール、デザインシステムはどれも、人が手で書くものとしてではなくビルドの成果物として HTML を吐きます。そしてディスク上のファイルとしては、たいてい事故で出会います。別の形式のはずのものを開いたら、サーバーがエラーページを返していた。そのときに見つかるのが HTML です。先頭 15 バイトを覚えておくことが、ファイルを扱う人にとってかなり役に立つ知識である理由がこれです。

HTML ファイルをこのサイトで開く

どれもブラウザ内で動きます。ファイルが端末から出ることはありません。

関連する形式

HTML と混同しやすい形式です。先頭バイトを見比べてください。

形式先頭バイト拡張子
XML固定の署名なし.xml
JSON固定の署名なし.json
SVG固定の署名なし.svg

よくある質問

ファイルのマジックナンバーとは何ですか?

ファイル先頭の数バイトで、名前が何であろうとその形式を示すものです。PNG は必ず 89 50 4E 47 で始まり、ZIP は 50 4B 03 04 で始まります。OS もブラウザも file コマンドも、拡張子を信用せずこのバイト列を読みます。だからファイル名を変えても中身の正体は変わりません。

マジックナンバーを自分で確認する方法は?

macOS や Linux なら xxd -l 16 ファイル名 で先頭 16 バイトが表示されます。Windows なら PowerShell で Format-Hex -Path ファイル名 -Count 16 です。あるいはこのサイトのファイル形式判別ツールにドロップすれば、ローカルでバイト列を読んで形式を教えてくれます。アップロードはありません。

2 つの形式が同じマジックナンバーを持つのはなぜ?

一方が他方の上に作られているからです。docx、xlsx、pptx、apk、jar、epub はすべて中身が ZIP アーカイブなので ZIP の署名で始まり、区別するには内部のエントリを見る必要があります。Java の class ファイルと macOS のユニバーサルバイナリはどちらも CA FE BA BE で始まり、直後のフィールドで見分けます。

マジックナンバーがまったくないファイルもある?

あります。XML、HTML、JSON、SVG、CSV といったテキストベースの形式には固定のバイト署名がないため、構造から判別します。先頭の <?xml 宣言、<svg 要素、あるいは { に続く引用符付きのキーなどです。これは確定ではなく推測なので、このリファレンスではそうした形式をプローブ判別として示しています。

マジックナンバーが一致すればそのファイルは安全?

いいえ。わかるのは形式であって、中身が無害かどうかではありません。正規の PDF や docx でも悪意のあるペイロードを含んでいることがあります。特に拡張子とバイト列が食い違うときに真の形式を知るのは有用な第一段階ですが、マルウェアスキャンではありません。

HTML かもしれないファイルをお持ちですか?ファイル形式判別ツールで調べる — マジックナンバーをローカルで読み取ります。アップロードは一切ありません。またはすべての形式を見る。