マークダウンとは
マークダウン(Markdown)は、記号を使って書式を指定する軽量マークアップの書き方で、2004年にジョン・グルーバー(John Gruber)がアーロン・スワーツの協力も得て考案しました。変換する前の原文そのものが読みやすいことを目標に設計されており、行頭の#は見出し、アスタリスク2つで囲んだ語は太字、行頭のハイフンは箇条書きになります。ただのテキストなので、どのエディターでも書け、バージョン管理で差分を取りやすく、チャットに貼っても崩れません。そのため、READMEや開発ドキュメント、ブログ、ノートアプリ、Wikiなどで広く使われています。
このページは、専用に書いた小さな変換処理でMarkdownをHTMLに変えます。結果欄はプレビューとHTMLソースを切り替えられ、文字数・行数・見出し数・リンク数と、危険として無効化したURLの数も確認できます。
この変換ツールが扱う文法
下の表の各行は、このページの変換処理で実際に確認した内容です。入力例の列をそのままエディターに貼って結果を見比べられます。
| 文法 | 入力例 | HTML | 備考 |
|---|---|---|---|
| 見出し | # 見出し1 ~ ###### 見出し6 | h1-h6 | #の後に空白が必要。下線形式(=== / ---)も可 |
| 太字・斜体 | **太字** *斜体* | strong, em | __太字__ や _斜体_ も可(単語の途中のアンダースコアは無視) |
| 取り消し線 | ~~削除~~ | del | GFMの拡張 |
| リスト | - 項目 / 1. 項目 | ul, ol | 空白2つ以上で入れ子。番号付きは開始番号を保持 |
| チェックリスト | - [x] 完了 / - [ ] 未完了 | input[type=checkbox] | GFMの拡張。プレビュー上では操作不可 |
| 表 | | 列1 | 列2 | + |:--|--:| | table | GFMの拡張。コロンで列の揃えを指定 |
| 引用 | > 引用文 | blockquote | 中にリストや見出しを入れ子にできる |
| コード | `code` / ```言語 ... ``` | code, pre | ~~~ のフェンスも可。中身は変換せずそのまま表示 |
| リンク・画像 | [文字](URL) /  | a, img | http・https・mailto・tel・相対パスのみ許可。引用符のタイトルも解釈 |
| 自動リンク | https://example.com / <https://...> | a | http・httpsのみ |
| 区切り線・改行 | --- / 行末に空白2つ | hr, br | 段落の直下の --- は見出しの下線として解釈される |
バックスラッシュによるエスケープにも対応しており、\*と書くとアスタリスクがそのまま表示されます。入れ子は8階層までで、それより深い部分は変換せず文字として表示します。表は、見出し行と区切り行の列数が同じときだけ表として認識されます。
非対応なのは、参照形式のリンク([文字][1])、脚注、定義リスト、数式、Mermaid図、インデントによるコードブロック(フェンスを使ってください)、生のHTMLブロックです。いずれも処理系ごとに挙動が分かれる領域なので、推測で変換すると公開先とずれたプレビューになってしまうためです。
CommonMark・GFM・元のMarkdown
元のMarkdownの説明は曖昧な部分が多く、同じ入力でも実装によって出力が違いました。その曖昧さをなくすために作られた仕様がCommonMarkで、この記事の執筆時点の最新版は2024年1月28日付の0.31.2です。GFM(GitHub Flavored Markdown)はCommonMarkに次の拡張を加えたものです。
- 表: パイプと区切り行で作ります。元の文法にはありませんでした。
- チェックリスト:
- [ ]と- [x]。 - 取り消し線:
~~文字~~。 - 自動リンク: URLを書くだけでリンクになります。
- タグのフィルタリング:
scriptやiframeなどの生HTMLタグを無効化します。
このツールは表・チェックリスト・取り消し線・自動リンクに対応しています。タグのフィルタリングは、そもそも生のHTMLを一切通さない作りなので不要です(下の安全性の節を参照)。
同じMarkdownでも表示が変わるところ
- 改行: CommonMarkでは文中の改行1つはスペース扱いで、強制改行には行末の空白2つ以上かバックスラッシュが必要です。コメント欄やチャットには改行をそのまま反映するものもあり、エディター上のチェックボックスがその挙動を再現します。
- 生のHTML: 多くの処理系は一部のHTMLを許可しますが、残るタグや属性は異なります。このツールはすべてエスケープして文字として見せます。
- 入れ子リストのインデント: 2スペースでよい処理系と4スペースが必要な処理系があります。番号付きリストは、マーカーの幅に合わせて3スペース以上にすると安全です。
- 見出しの空白:
#見出しのように空白がないと見出しにならない処理系が大半です。必ず# 見出しと書きましょう。 - 表の列数: 見出し行と区切り行の列数が違うと表になりません。
実際の出力で確認した例
例1: 列揃えのある表 見出し2列、区切り行に:--(左)と--:(右)、データ2行を入力しました。表はスクロール用のコンテナで包まれ、各見出しセルとデータセルにtext-alignが付きます。
| 名前 | 点数 |
|:--|--:|
| 佐藤 | 90 |
| 鈴木 | 85 |<div class="md-table-wrap"><table>
<thead><tr><th style="text-align:left">名前</th><th style="text-align:right">点数</th></tr></thead>
<tbody>
<tr><td style="text-align:left">佐藤</td><td style="text-align:right">90</td></tr>
<tr><td style="text-align:left">鈴木</td><td style="text-align:right">85</td></tr>
</tbody></table></div>例2: 入れ子のリスト 「りんご」「なし」を2スペース字下げすると、「果物」項目の中の子リストになります。最上位は2項目、入れ子は2項目です。
- 果物
- りんご
- なし
- 野菜<ul>
<li>果物
<ul>
<li>りんご</li>
<li>なし</li>
</ul></li>
<li>野菜</li>
</ul>安全性: 変換後のHTMLをそのまま信用してはいけない理由
Markdownは原文にHTMLを混ぜられるため、利用者が入力したMarkdownをそのままページのHTMLにすると、クロスサイトスクリプティング(XSS)の脆弱性になりがちです。典型的な手口は3つあります。
- scriptタグやイベント属性: 画像タグの
onerrorにコードを入れる手口です。 - 危険なURLスキーム: リンク先を
javascript:で始める手口です。 - 属性からの脱出: URLの中の引用符で
href属性を閉じ、新しい属性を差し込む手口です。
このツールは3つともコードで対処しています。まず&・<・>と引用符を文字参照に置き換え、入力中の生HTMLが要素にならず文字として表示されるようにします。リンクと画像のURLはhttp・https・mailto・telと相対パス、アンカーだけを許可し、それ以外は#に置き換えます。スキームを隠すために入れた制御文字も判定前に取り除きます。外部リンクにはrel="noopener noreferrer nofollow"を付けて新しいタブで開きます。プレビュー欄には生成したHTMLをそのまま入れているため、この処理が唯一の防御で、別のサニタイズ用ライブラリは使っていません。セキュリティテスト用サンプルでは、javascript:のリンクとdata:の画像が#に置き換わって「ブロックしたURL」が2になり、scriptタグと画像タグは文字として表示されます。
自分の文章を自分で確認するプレビュー用途なら十分ですが、本番コードの手本ではありません。手書きの変換処理は想定外の組み合わせで破られる可能性があります。他人が書いたMarkdownを表示するサービスでは、メンテナンスされているMarkdownライブラリと専用のHTMLサニタイザーを併用し、可能ならコンテンツセキュリティポリシー(CSP)も設定してください。
よくある間違い
- リストの前に空行がない: 直前の段落に続けて書くとリストにならない処理系があります。空行を1つ入れましょう。
- コードフェンスの数の不一致: 開きが3個で閉じが2個だと、以降がすべてコード扱いになります。コード内にバッククォート3個があるなら、外側を4個にします。
- 意図しない強調: 数式やファイル名の
*や_が斜体になることがあります。バックスラッシュでエスケープしてください。 - URLの空白や括弧: 空白は
%20にします。対になった括弧は使えますが、片側だけの括弧は避けてください。 - 画像の代替テキストが空:
では、スクリーンリーダー利用者や画像が表示されない環境に情報が伝わりません。
限界と、使うべきでない場面
これは基本文法とGFMの一部を扱う簡易変換ツールで、CommonMarkの全テストを通るものではありません。入れ子のリストの中のコードブロックのような複雑な構造は、GitHubやお使いの静的サイトジェネレーターと結果が異なることがあります。外部URLの画像はプレビュー時にそのサーバーへアクセスするため、IPアドレスが相手に伝わる可能性があります。公開前には、GitHubやブログ、ノートアプリなど実際の公開先のプレビューで最終確認してください。パスワードや個人情報は、このツールが送信しなくても、オンラインツールには入力しない習慣をつけておくと安心です。
結果の使い方
- 左で書きながら、見出しのレベルが飛んでいないか(h1の次がいきなりh3など)を確認します。
- 「HTMLソース」タブで変換結果を確認し、必要なら「HTMLをコピー」で持ち出します。貼り付け先のサービスは独自のサニタイズを行う点に注意してください。
- READMEやWikiに載せる文書は、公開先の表示で表・チェックリスト・改行が同じか確認します。
- 文字数の確認には文字数カウント、2つの版の比較にはテキスト差分チェッカー、英字の大文字小文字の整形には大文字小文字変換が使えます。
- Markdownに載せるJSONのサンプルは、先にJSON整形ツールで整えておくと見やすくなります。
よくある質問
変換したHTMLをコピーできますか?
はい。「HTMLソース」タブで内容を確認でき、「HTMLをコピー」ボタンでコピーできます。ほかのサービスに貼り付けると、そのサービスのサニタイズ規則で残るタグや属性が決まります。
表はどう書きますか?
1行目に| 列1 | 列2 |の形で見出しを書き、2行目に|--|--|のような区切り行、その後にデータ行を続けます。区切り行で:--は左揃え、:-:は中央揃え、--:は右揃えです。どの行も見出しと同じ列数にしてください。
MarkdownにHTMLタグを直接書いてもいいですか?
このツールでは安全のため、HTMLタグは変換せず文字として表示します。GitHubなどでは許可されたタグだけが残りますが、許可の範囲は公開先ごとに違うため、実際の公開先で確認してください。
改行しても1行につながってしまいます。
CommonMarkでは改行1つでは段落内でつながります。行末に空白を2つ入れるか、空行で段落を分けてください。エディター上の「改行をそのまま <br> にする」をオンにすると、改行を反映する環境での見え方を確かめられます。
入力した内容はサーバーに送られますか?
いいえ。変換はブラウザ内のスクリプトだけで行われ、入力内容は送信されません。ただし、Markdownに外部画像のURLを入れると、プレビューがその画像を読み込むためにそのサーバーへアクセスします。
MarkdownとHTMLはどちらで書くべきですか?
READMEやブログ、メモのような文章中心の文書ならMarkdownが手軽です。細かいレイアウトや色、フォームが必要ならHTMLが向いています。Markdownに少しだけHTMLを混ぜる方法もありますが、処理系が許可している場合に限られます。