はじめに
【WordPress×PHP】WP_Queryで記事一覧を作るでは、好きな条件で記事一覧を自作する方法を解説しました。
ここまでの実務サンプルでは、タイトルを出力するときにesc_html( get_the_title() )、URLを出力するときにesc_url( get_permalink() )のように、esc_で始まる関数を使ってきました。今回は、このエスケープが何のために必要なのかを、実際に「エスケープしなかったらどうなるか」を見ながら解説します。
このシリーズの全15回の構成は、第1回の記事の冒頭にまとめています。
エスケープしないと何が起きるか
URLの最後に?nick=くるみと付けると「ようこそ、くるみさん」と表示する、という機能を考えてみます。$_GET['nick']で受け取った値を、そのままechoするコードです。
// 悪い例:受け取った値をそのまま出力している
add_action( 'swell_before_post_head', function() {
if ( isset( $_GET['nick'] ) ) {
echo '<p>ようこそ、' . $_GET['nick'] . 'さん</p>';
}
} );普通の名前なら問題ありません。ところが、ローカル環境でnickにHTMLのタグ(文字を赤く大きくする<b>タグ)を入れたURLを開くと、次のように表示されました。

URLに入れた文字列が、HTMLのタグとしてページの一部になってしまいました。このURLを誰かに送れば、そのサイトに偽のお知らせを表示させることができます。タグの代わりにJavaScriptを埋め込めば、ページを見た人のブラウザでプログラムを動かすこともできてしまいます。これをクロスサイトスクリプティング(XSS)と呼びます。
同じURLで、esc_html()を通して出力すると、こうなりました。
echo '<p>ようこそ、' . esc_html( $_GET['nick'] ) . 'さん</p>';
タグは効かず、ただの文字として表示されました。
エスケープとは?
エスケープは、HTMLで特別な意味を持つ記号を、「ただの文字」として表示される書き方に置き換えることです。esc_html()の結果をローカル環境で確かめると、次のように置き換わっていました。
| 元の文字 | エスケープ後 |
|---|---|
< | < |
> | > |
" | " |
' | ' |
& | & |

ブラウザは<を「<という文字」として表示するので、タグとして解釈されることがなくなります。
出力する場所に合わせて関数を選ぶ
WordPressには、出力する場所に合わせたエスケープ関数が用意されています。

| 関数 | 使う場所 | 例 |
|---|---|---|
esc_html() | タグの間に表示する文字 | <p><?php echo esc_html( $text ); ?></p> |
esc_attr() | 属性の値 | <input value="<?php echo esc_attr( $value ); ?>"> |
esc_url() | URL(href・src) | <a href="<?php echo esc_url( $url ); ?>"> |
esc_textarea() | <textarea>の中 | |
wp_kses_post() | 一部のタグを残したいHTML | 記事本文で使えるタグだけ残す |
ローカル環境で、それぞれの動きを確かめました。
- esc_attr():
"が"に置き換わるため、属性の値の外に抜け出して、別の属性を書き足されることがない - esc_url():
https://example.com/?a=1&b=2は&が&に置き換わった。JavaScriptを実行させる形式のURLは、空の文字列になった。また、httpなどのないexample.com/pageには、http://が補われた - wp_kses_post():
<strong>や<a>は残り、記事本文で使えないタグは取り除かれた
SWELLのsearchform.phpでも、フォームの送信先URLはesc_url( home_url( '/' ) )、プレースホルダーの文字はesc_attr( $placeholder )と、場所に合わせて使い分けています。SWELLのテーマフォルダ全体では、esc_attr(が481回、esc_html(が124回、esc_url(が72回使われていました。
入力はサニタイズ、出力はエスケープ
エスケープとよく似た言葉にサニタイズがあります。
- サニタイズ:受け取った値を、保存や計算に使える正しい形に整えること(入力のとき)
- エスケープ:値を、表示する場所で安全な形に置き換えること(出力のとき)

ローカル環境での結果です。
| 関数 | 入力 | 結果 |
|---|---|---|
sanitize_text_field() | タグと改行を含む文字列 | タグと改行が取り除かれた |
absint() | '12abc' | 12 |
absint() | '-5' | 5 |
また、WordPressでは$_GETや$_POSTの値に、自動で\(バックスラッシュ)が付きます。ローカル環境でも、"の前に\が付いていました。URLやフォームの値を使うときは、wp_unslash()で\を外してからサニタイズするのが定番の書き方です。
$nick = isset( $_GET['nick'] ) ? sanitize_text_field( wp_unslash( $_GET['nick'] ) ) : '';
echo '<p>ようこそ、' . esc_html( $nick ) . 'さん</p>';サニタイズした値でも、出力するときにはエスケープします。「どこから来た値か」で判断せず、出力する直前に毎回エスケープするのが、WordPressの基本の考え方です。
WordPressの関数には、最初からエスケープされた値を返すものもあります。たとえばget_search_query()(検索キーワード)は、エスケープ済みの値を返しました。エスケープ前の値が必要なときはget_search_query( false )と書きます。二重にエスケープすると&のような表示になるので、関数の説明を確認して使い分けましょう。
管理画面でデータを変える処理には「権限」と「nonce」
今回はエスケープが中心ですが、フォームから設定を保存するような処理を自作するときは、次の2つも必要になります。
- 権限の確認:
current_user_can( 'manage_options' )のように、操作してよいユーザーかを確認する - nonce(ナンス)の確認:
wp_nonce_field()でフォームに合言葉を埋め込み、受け取ったときにwp_verify_nonce()で確かめる。ほかのサイトから勝手に送信されたリクエストを防ぐ
SWELLの中でも、current_user_can(が18回、wp_nonce_field(が3回使われていました。このシリーズの範囲(表示のカスタマイズ)では使う場面は少ないですが、「データを変える処理には、権限とnonce」と覚えておきましょう。
実務サンプル:検索結果が0件のときにキーワード入りのメッセージを表示する
SWELLでは、検索結果が0件のとき「記事が見つかりませんでした。」と表示されます。これを、検索キーワードを含めたメッセージと、Googleでサイト内を検索するリンクに変えてみましょう。
SWELLのparts/post_list/loop_main.phpには、0件のときの文章を変えられるswell_post_list_404_textというフィルター(第10回)があります。子テーマのfunctions.phpに、次のコードを追加します。
/**
* 検索結果が0件のとき、キーワード入りのメッセージとGoogleでのサイト内検索リンクを表示する
*/
add_filter( 'swell_post_list_404_text', function( $html ) {
if ( ! is_search() ) {
return $html;
}
$keyword = get_search_query( false ); // エスケープ前のキーワード
$host = wp_parse_url( home_url(), PHP_URL_HOST );
$google = 'https://www.google.com/search?q=' . rawurlencode( 'site:' . $host . ' ' . $keyword );
return '<div class="kurumico-noresult">'
. '<p>「' . esc_html( $keyword ) . '」に一致する記事は見つかりませんでした。</p>'
. '<p>別のキーワードで検索するか、<a href="' . esc_url( $google ) . '" target="_blank" rel="noopener">Googleでこのサイト内を検索</a>してみてください。</p>'
. '</div>';
} );子テーマのstyle.cssには、次のCSSを追加します。
.kurumico-noresult {
padding: 1.5em;
background: #f7f9fb;
border: 1px solid #dde3ea;
border-radius: 6px;
}
.kurumico-noresult p + p {
margin-top: .5em;
}記事のないキーワード「ショートコード」で検索すると、次のように表示されました。

タグや"、&を含む文字列で検索しても、メッセージの中ではすべて文字として表示され、ページが崩れることはありませんでした。
コードのポイントは次のとおりです。
- SWELLは、このフィルターが返したHTMLをエスケープせずにそのまま出力する。そのため、返すHTMLの中で自分でエスケープしている
- キーワードはタグの間に表示するので
esc_html()、リンク先はURLなのでesc_url()を使っている get_search_query( false )でエスケープ前の値を受け取り、esc_html()で1回だけエスケープしている(二重エスケープを防ぐ)- URLの中に入れるキーワードは、
rawurlencode()でURL用の形に変換している - 検索以外のページ(カテゴリーに記事がない場合など)では、受け取った
$htmlをそのまま返している
よくある質問・エラー対処
Q. 自分で書いた文字列(固定の文章)もエスケープが必要ですか?
→ '記事一覧'のように、コードの中に書いた固定の文字列は、外から変えられないのでエスケープしなくても安全です。ただし、WordPressの公式のルールでは「出力するものはすべてエスケープ」が推奨されています。迷ったら、エスケープしておきましょう。
Q. URLパラメーターを付けたら、ページが「見つかりません」になりました
→ パラメーター名が、WordPressが使っている名前と重なっている可能性があります。ローカル環境で?name=くるみを付けたところ、記事ページが404エラーになりました(nameは、WordPressが記事のスラッグに使う名前です)。pやsなども、WordPressが使っている名前です。自作のパラメーターには、kurumico_nickのような重ならない名前を付けましょう。
おわりに(まとめ)
エスケープは、外から来た文字列がページを書き換えてしまうのを防ぐ、もっとも基本的なセキュリティ対策です。
- ✅ エスケープしないと、URLなどから入れた文字列がHTMLとして効いてしまう(XSS)
- ✅ 表示する文字はesc_html()、属性はesc_attr()、URLはesc_url()
- ✅ 入力はサニタイズ、出力はエスケープ。出力の直前に毎回エスケープする
- ✅ $_GETや$_POSTの値は、wp_unslash()してからサニタイズする
- ✅ エスケープ済みの値を返す関数もあるので、二重エスケープに注意する
- ✅ データを変える処理には、権限の確認とnonceも必要
次回は、記事の中に[ ]で書いて呼び出せる「ショートコード」の作り方を解説します。
次回は、ショートコードの作り方について解説予定です。
この記事が、少しでも誰かのお役に立てれば幸いです。
関連記事
※1回買えば複数サイトで使い回しOK
当サイトの記事で使用したVBAなどのサンプルをDLできます
この記事のサンプルはありません!
ダウンロードページへは下のカードをクリックすればジャンプできます。
よろしければご利用ください!


