2026年10月11日
2026年10月11日
WordPressのwp_kses_post()とwp_kses_allowed_html()で許可するHTMLをカスタマイズする方法
wp_kses_post() で消えてしまうタグや属性を通したいときは、wp_kses_allowed_html フィルターで許可リスト全体を広げるより、wp_kses_allowed_html( 'post' ) で取得した配列をコピーして必要な分だけ足し、その出力箇所だけ wp_kses() に渡すほうが安全です。この記事では、2つの関数の関係、許可リストの配列の読み方、用途を絞った追加方法、フィルターで全体を変える場合の注意点、style 属性やURLのプロトコルの扱いまでを解説します。
2つの関数の関係
- **
wp_kses( $content, $allowed_html, $allowed_protocols = array() )**:HTMLを解析し、許可リストにないタグと属性を取り除く本体の関数です。2つ目の引数には、許可リストの配列か、文脈名('post'など)の文字列を渡せます。 - **
wp_kses_post( $data )**:wp_kses( $data, 'post' )の省略形です。投稿本文で使ってよいHTMLを基準に、それ以外を取り除きます。 - **
wp_kses_allowed_html( $context )**:文脈名を受け取り、その文脈の許可リスト(配列)を返します。'post'、'data'(コメントなどで使う最小限のタグ)、'strip'(タグをすべて除去)、'entities'などがあります。返す直前にwp_kses_allowed_htmlフィルターを通ります。
つまり、wp_kses_post() が通すHTMLを変えたいなら、wp_kses_allowed_html フィルターで 'post' 文脈の配列を変えることになります。ただしこれは、その文脈を使うすべての場所に影響します。次の章で、影響範囲を絞る方法から説明します。
許可リストの配列の形
許可リストは「タグ名 => 属性の配列」の形をした連想配列です。属性の値に true を入れると、その属性を許可する意味になります。
array(
'a' => array(
'href' => true,
'target' => true,
'rel' => true,
),
'img' => array(
'src' => true,
'alt' => true,
'width' => true,
'height' => true,
),
);
'post' 文脈では、多くのタグに class、id、style、title、role、aria-*、data-* などの共通属性が自動で足されます。一方で、iframe、script、form 系の入力要素、SVG の要素などは標準では含まれません。実際の内容はWordPressのバージョンで変わるため、使っている環境で次のように確認できます。
wp eval 'print_r( array_keys( wp_kses_allowed_html( "post" ) ) );'
方法1:出力箇所だけ許可リストを広げる(推奨)
テーマやプラグインで、カスタムフィールドの値や設定画面の入力など特定のHTMLを出力するときは、その場所専用の許可リストを作ります。他の場所の挙動を変えずに済むためです。
例として、管理者が入力した地図の埋め込み(iframe)を、許可した属性だけ残して出力します。
/**
* 地図埋め込み用の許可リスト。
*/
function my_map_allowed_html() {
$allowed = wp_kses_allowed_html( 'post' );
$allowed['iframe'] = array(
'src' => true,
'width' => true,
'height' => true,
'title' => true,
'loading' => true,
'allowfullscreen' => true,
'referrerpolicy' => true,
);
return $allowed;
}
/**
* iframe の src を許可したホストに限る。
*/
function my_map_embed_html( $html ) {
$clean = wp_kses( $html, my_map_allowed_html() );
if ( preg_match_all( '/<iframe[^>]+src="([^"]+)"/i', $clean, $m ) ) {
foreach ( $m[1] as $src ) {
$host = wp_parse_url( html_entity_decode( $src ), PHP_URL_HOST );
if ( 'www.google.com' !== $host ) {
return ''; // 想定外の埋め込みは出力しない。
}
}
}
return $clean;
}
// テンプレートでの使い方。
echo my_map_embed_html( get_post_meta( get_the_ID(), 'map_embed', true ) );
ここで大事なのは、wp_kses() はタグと属性の種類を絞るだけで、src にどのサイトのURLが入っているかまでは見ないという点です。iframe を許可すると、任意のサイトをページに埋め込めるようになります。上の例では、取り出した src のホストを確認して、想定外なら出力しないようにしています。ホスト名は使うサービスに合わせて変えてください。
方法2:フィルターで「post」文脈そのものを変える
投稿者や寄稿者が本文に書いたタグが保存時に消えてしまい、それを通したい場合は、フィルターで 'post' 文脈を変えます。
add_filter( 'wp_kses_allowed_html', function ( $tags, $context ) {
if ( 'post' !== $context ) {
return $tags;
}
// details / summary を許可する例(版によってはすでに許可済み)。
$tags['details'] = array( 'open' => true, 'class' => true );
$tags['summary'] = array( 'class' => true );
return $tags;
}, 10, 2 );
この方法を選ぶ前に、次の2点を理解しておく必要があります。
影響範囲が広い:'post' 文脈は、unfiltered_html 権限を持たないユーザーが投稿を保存するときの本文・抜粋のフィルター、wp_kses_post() を使うテーマやプラグインのあらゆる出力に使われます。iframe や script に相当する危険なタグをここで許可すると、寄稿者がXSS(クロスサイトスクリプティング)を仕込める状態になります。足してよいのは、イベント属性(onclick など)やスクリプトを持たない、表示用のタグと属性に限ります。
管理者には効かない場面がある:シングルサイトの管理者と編集者は標準で unfiltered_html 権限を持つため、保存時の kses フィルターがかかりません。「管理者では残るのに投稿者では消える」という症状は、この権限の差が原因です。マルチサイトでは特権管理者だけがこの権限を持ちます。wp-config.php で DISALLOW_UNFILTERED_HTML を true にすると、管理者を含めて全員の入力が kses を通るようになります。
ブロックエディターでは、許可されていない属性を含むブロックを投稿者が保存すると、属性が取り除かれ、再編集時にブロックの検証エラーが表示されることがあります。カスタムブロックを作る場合は、保存されるHTMLが投稿者の権限で残るかどうかもテストしてください。
style 属性とURLのプロトコル
style 属性:style を許可しても、中身のCSSは safecss_filter_attr() で絞り込まれ、許可されたプロパティ以外は取り除かれます。プロパティを足したいときは safe_style_css フィルターを使います。
add_filter( 'safe_style_css', function ( $properties ) {
$properties[] = 'aspect-ratio';
return $properties;
} );
URLのプロトコル:href や src に使えるプロトコル(http、https、mailto、tel など)は wp_allowed_protocols() が返す一覧で決まり、kses_allowed_protocols フィルターで変更できます。javascript: は許可されません。独自のアプリ連携用スキームを足す場合も、必要なものだけにします。
つまずきやすいポイントと確認方法
- 出力時のエスケープとの役割分担:
wp_kses_post()は「HTMLとして出してよい値」に使う関数です。属性値にはesc_attr()、URLにはesc_url()、ただの文字列にはesc_html()を使い、HTMLを通す必要がない場所でwp_kses_post()を使わないようにします。 - 処理の重さ:kses はHTMLを解析するため、大きなHTMLを1ページで何度も通すと遅くなります。同じ値を繰り返し出すなら、一度処理した結果を変数に入れて使い回します。
- 保存時と出力時:保存時に kses を通しても、出力時のエスケープは省略しないでください。データベースの値が別経路で書き換えられる可能性があるためです。
確認は、投稿者権限のテストユーザーでログインし、許可したタグと許可していないタグ・属性(onclick など)を混ぜた本文を保存して、保存後のHTMLを「コードエディター」表示で見る方法が分かりやすいです。コードを入れる前にはバックアップを取り、ステージング環境で試してください。標準の許可リストの中身や共通属性は、WordPressのバージョンアップで追加・変更されることがあります(2026年10月現在)。
まとめ
wp_kses_post()はwp_kses( $data, 'post' )の省略形で、許可リストはwp_kses_allowed_html( 'post' )が返す配列で決まる。- 特定の出力だけ広げたいなら、配列をコピーして足し、その場所だけ
wp_kses()に渡す。iframeなどはsrcのホストも別に確認する。 wp_kses_allowed_htmlフィルターで'post'を変えると、投稿者の保存時を含むあらゆる場所に影響する。表示用のタグと属性だけに限る。- 管理者と投稿者で結果が違うのは
unfiltered_html権限の差。style の中身はsafe_style_css、プロトコルはkses_allowed_protocolsで調整する。