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 で調整する。

お気軽にご相談ください

お見積りへ お問い合わせへ
▲