headタグの整理とセキュリティ対策 – WordPress実践制作【WPCafe編】
- 2025/10/25
- PRACTICE
- カフェサイトを作ろう
- 0comments
- 12views
- 約28分で読めます
- 10
前回、フッターを表示するでフッターの実装を完了しました。
今回は、WordPressが自動的に出力するheadタグの整理(デフォルト機能を無効化)と、サイトのセキュリティを強化していきます。
目次
なぜデフォルト機能を無効化するのか
WordPressはデフォルトで多くの機能を有効にしています。これらを無効化する理由を理解しましょう。
WordPressが多くの情報を出力する理由
デフォルト機能を無効化する目的
デフォルト機能を無効化する最大の目的は、WordPressが自動出力するタグを削除し、自分で必要なタグを適切な順序で出力できるようにすることです。
WordPressは様々なスクリプトやスタイルを自動的に読み込みますが、プラグインとの互換性を保つため、その順序や出力場所を完全にはコントロールできません。不要なものを削除し、必要なものだけを自分で記述することで、以下のメリットがあります。
- 読み込み順序の制御:スタイルやスクリプトを思い通りの順序で出力
- パフォーマンスの最適化:必要なものだけを必要なタイミングで読み込む
- HTMLの軽量化:不要なコードを削除してページ速度を向上
- コードの可読性向上:シンプルで管理しやすいheadタグ
今後のサイト制作でも使える対策
今回の対策は、このカフェサイトだけでなく、これから作るすべてのWordPressサイトで必要になる基本的な設定です。
企業サイト、ECサイト、ポートフォリオサイトなど、どのようなサイトを作る場合でも、デフォルト機能を無効化とセキュリティ対策は必須です。一度この仕組みを理解しておけば、今後のサイト制作で毎回活用できる重要なノウハウになります。
それでは、実際にファイルを作成していきましょう。
WordPressのデフォルト機能を無効化する
デフォルト機能の無効化はfunctions.phpで行う
WordPressのデフォルト機能を無効化するには、functions.phpでWordPressの関数を使って制御します。
WordPressはwp_head()関数を実行すると、様々な情報を自動的にheadタグに出力します。この出力を制御するために、functions.phpにremove_action()やremove_filter()といった関数を記述します。
ファイルを分割して管理
functions.phpに直接記述することもできますが、今回はfunctions-theme-reset.phpという専用ファイルを作成し、functions.phpから読み込む形にします。
前回の記事でfunctions-theme-contents.phpを作成したように、機能ごとにファイルを分けることで、コードの管理がしやすくなります。
テーマディレクトリにfunctions-theme-reset.phpを作成します。
functions.phpで読み込み
functions.phpに以下を追加して、作成したファイルを読み込みます。
<?php
// デフォルトの無効化
require_once 'functions-theme-reset.php';
// 初期化
require_once 'functions-theme-init.php';
// JS/CSS読み込み
require_once 'functions-theme-load.php';
// コンテンツ関連
require_once 'functions-theme-contents.php';PHP基本的な設定であるfunctions-theme-reset.phpを最初に読み込み、その後に初期化やコンテンツ関連の処理を読み込みます。
functions-theme-reset.phpのコード
functions-theme-reset.phpに以下のコードを記述します。
<?php
//////////////////////////////////////////////////
// 絵文字(Emoji)関連のスクリプトとスタイルの読み込みを無効化
//////////////////////////////////////////////////
remove_action( 'wp_head', 'print_emoji_detection_script', 7 );
remove_action( 'admin_print_scripts', 'print_emoji_detection_script' );
remove_action( 'wp_print_styles', 'print_emoji_styles' );
remove_action( 'admin_print_styles', 'print_emoji_styles' );
remove_filter( 'the_content_feed', 'wp_staticize_emoji' );
remove_filter( 'comment_text_rss', 'wp_staticize_emoji' );
remove_filter( 'wp_mail', 'wp_staticize_emoji_for_email' );
//////////////////////////////////////////////////
// インデックスページへのrelリンクを削除
//////////////////////////////////////////////////
remove_action( 'wp_head', 'index_rel_link' );
//////////////////////////////////////////////////
// 隣接する投稿へのrelリンク(prev/next)を削除
//////////////////////////////////////////////////
remove_action( 'wp_head', 'parent_post_rel_link', 10, 0 );
remove_action( 'wp_head', 'start_post_rel_link', 10, 0 );
remove_action( 'wp_head', 'adjacent_posts_rel_link_wp_head', 10, 0 );
//////////////////////////////////////////////////
// canonical(正規URL)タグを削除
//////////////////////////////////////////////////
remove_action( 'wp_head', 'rel_canonical' );
//////////////////////////////////////////////////
// oEmbedの自動ディスカバリーリンクを削除
//////////////////////////////////////////////////
remove_action('wp_head','wp_oembed_add_discovery_links');
//////////////////////////////////////////////////
// DNSプリフェッチの削除(外部DNSアクセスを抑制)
//////////////////////////////////////////////////
function remove_dns_prefetch( $hints, $relation_type ) {
if ( 'dns-prefetch' === $relation_type ) {
// 全てのDNSプリフェッチを除去
return [];
}
return $hints;
}
add_filter( 'wp_resource_hints', 'remove_dns_prefetch', 10, 2 );
//////////////////////////////////////////////////
// フロントエンドで不要なスタイルの読み込みを解除する
//////////////////////////////////////////////////
function remove_my_global_styles() {
// 管理画面の場合は何もしない
if ( is_admin() ) return;
// ブロックテーマのグローバルスタイルを解除
wp_dequeue_style('global-styles');
// クラシックテーマのスタイルを解除
wp_dequeue_style('classic-theme-styles');
// WP-PageNaviプラグインのCSSを解除(プラグイン使用時のみ)
if ( function_exists('wp_pagenavi') ) {
wp_dequeue_style('wp-pagenavi');
}
// 非ログインユーザーにDashiconsの読み込みを停止(管理者権限のみ読み込み)
if ( ! current_user_can('update_core') ) {
wp_deregister_style('dashicons');
}
}
add_action('wp_enqueue_scripts', 'remove_my_global_styles', 20);PHPコードの解説
各コードが「何のために」必要なのか解説します。
絵文字関連のスクリプトとスタイルの無効化
remove_action( 'wp_head', 'print_emoji_detection_script', 7 );
remove_action( 'admin_print_scripts', 'print_emoji_detection_script' );
remove_action( 'wp_print_styles', 'print_emoji_styles' );
remove_action( 'admin_print_styles', 'print_emoji_styles' );
remove_filter( 'the_content_feed', 'wp_staticize_emoji' );
remove_filter( 'comment_text_rss', 'wp_staticize_emoji' );
remove_filter( 'wp_mail', 'wp_staticize_emoji_for_email' );PHPWordPressは古いブラウザで絵文字を表示するためのスクリプトを自動的に読み込みます。現代のブラウザでは不要なため、削除してページ速度を向上させます。
インデックスページへのrelリンクを削除
remove_action( 'wp_head', 'index_rel_link' );PHPトップページへのrelリンクが出力されますが、SEO効果はなく不要です。headタグをシンプルにするために削除します。
隣接する投稿へのrelリンク(prev/next)を削除
remove_action( 'wp_head', 'parent_post_rel_link', 10, 0 );
remove_action( 'wp_head', 'start_post_rel_link', 10, 0 );
remove_action( 'wp_head', 'adjacent_posts_rel_link_wp_head', 10, 0 );PHP前後の投稿へのrelリンクが出力されますが、今回のような店舗・企業サイトでは「前の投稿」「次の投稿」という概念が不要です。ブログのように記事を時系列で読むサイトではないため、削除してHTMLを軽量化します。
canonical(正規URL)タグを削除
remove_action( 'wp_head', 'rel_canonical' );PHP今後All in One SEOなどのSEOプラグインを導入する予定のため、WordPress標準のcanonicalタグを削除します。プラグイン側でcanonicalタグを出力するため、重複を避けるために標準のものは不要です。
oEmbedの自動ディスカバリーリンクを削除
remove_action('wp_head','wp_oembed_add_discovery_links');PHPoEmbedは外部サイトが自サイトのコンテンツを埋め込む機能です。必要ない場合は削除してheadタグをシンプルにします。
DNSプリフェッチの削除
function remove_dns_prefetch( $hints, $relation_type ) {
if ( 'dns-prefetch' === $relation_type ) {
return [];
}
return $hints;
}
add_filter( 'wp_resource_hints', 'remove_dns_prefetch', 10, 2 );PHPWordPressは自動的にs.w.org(絵文字用のCDN)へのDNSプリフェッチを追加します。絵文字機能を無効化している場合は不要なため削除します。
フロントエンドで不要なスタイルの読み込みを解除
function remove_my_global_styles() {
// 管理画面の場合は何もしない
if ( is_admin() ) return;
// ブロックテーマのグローバルスタイルを解除
wp_dequeue_style('global-styles');
// クラシックテーマのスタイルを解除
wp_dequeue_style('classic-theme-styles');
// WP-PageNaviプラグインのCSSを解除(プラグイン使用時のみ)
if ( function_exists('wp_pagenavi') ) {
wp_dequeue_style('wp-pagenavi');
}
// 非ログインユーザーにDashiconsの読み込みを停止(管理者権限のみ読み込み)
if ( ! current_user_can('update_core') ) {
wp_deregister_style('dashicons');
}
}
add_action('wp_enqueue_scripts', 'remove_my_global_styles', 20);PHPWordPressが自動的に読み込むスタイル(ブロックテーマのグローバルスタイル、クラシックテーマのスタイル、Dashiconsアイコンフォントなど)は、今回のようなオリジナルデザインのサイトでは不要です。これらを削除することで、CSSファイルの読み込みを減らし、ページの読み込み速度を向上させます。特にDashiconsは管理者以外には不要なため、非ログインユーザーには読み込まないようにします。
表示の確認
セキュリティ対策を実装する
WordPressのデフォルト設定とセキュリティ
WordPressは、様々なプラグインや外部サービスとの連携を容易にするため、バージョン情報やREST API、XML-RPCなどの機能をデフォルトで提供しています。これらは本来、開発者や外部ツールにとって便利な機能です。
しかし、これらの情報は攻撃者にとっても有益な情報になってしまいます。例えば、バージョン情報から既知の脆弱性を特定したり、REST APIを利用してユーザー名を収集したりすることができます。
そのため、実際の運用では必要のない機能を無効化し、攻撃の手がかりになる情報を隠すことが重要です。適切な対策を施すことで、これらのリスクを大幅に軽減できます。
セキュリティ対策もfunctions.phpで行う
これらのセキュリティ対策も、デフォルト機能を無効化と同様にfunctions.phpでWordPressの関数を使って制御します。add_filter()やremove_action()などの関数を使って、WordPressの動作を変更します。
今回はfunctions-theme-security.phpという専用ファイルを作成し、functions.phpから読み込む形で管理します。
ファイルを作成
テーマディレクトリにfunctions-theme-security.phpを作成します。
functions.phpで読み込み
functions.php全体は以下のようになります。
<?php
// デフォルトの無効化
require_once 'functions-theme-reset.php';
// セキュリティ対策
require_once 'functions-theme-security.php';
// 初期化
require_once 'functions-theme-init.php';
// JS/CSS読み込み
require_once 'functions-theme-load.php';
// コンテンツ関連
require_once 'functions-theme-contents.php';PHPfunctions-theme-security.phpのコード
functions-theme-security.phpに以下のコードを記述します。
<?php
//////////////////////////////////////////////////
// WPバージョン情報の非表示
//////////////////////////////////////////////////
remove_action('wp_head', 'wp_generator');
//////////////////////////////////////////////////
// ログインエラーメッセージの抑制
//////////////////////////////////////////////////
add_filter('login_errors', function () {
return 'ログイン情報が正しくありません。';
});
//////////////////////////////////////////////////
// XML-RPC の無効化
//////////////////////////////////////////////////
add_filter('xmlrpc_enabled', '__return_false');
//////////////////////////////////////////////////
// REST APIエンドポイントを示すlinkタグを削除
//////////////////////////////////////////////////
remove_action('wp_head','rest_output_link_wp_head');
//////////////////////////////////////////////////
// REST API リンクを HTTPヘッダからも削除
//////////////////////////////////////////////////
remove_action( 'template_redirect', 'rest_output_link_header', 11 );
//////////////////////////////////////////////////
// ユーザー列挙(/?author=1 など)の無効化
//////////////////////////////////////////////////
if (!is_admin()) {
add_action('init', function () {
if (preg_match('/author=([0-9]*)/i', $_SERVER['QUERY_STRING'])) {
wp_redirect(home_url());
exit;
}
});
}
//////////////////////////////////////////////////
// ヘッダーから不要な情報を削除
//////////////////////////////////////////////////
remove_action('wp_head', 'rsd_link');
remove_action('wp_head', 'wlwmanifest_link');
remove_action('wp_head', 'wp_shortlink_wp_head');
remove_action('wp_head', 'feed_links', 2);
remove_action('wp_head', 'feed_links_extra', 3, 0);
//////////////////////////////////////////////////
// コメント内の HTML タグ制限(特定タグ無効化)
//////////////////////////////////////////////////
//add_filter('pre_comment_content', function ($content) {
// return esc_html($content);
//});
//////////////////////////////////////////////////
// コメントのコードを<code>化
//////////////////////////////////////////////////
add_filter('comment_text', function ($comment_text) {
return '<pre><code>' . esc_html($comment_text) . '</code></pre>';
}, 10, 1);
//////////////////////////////////////////////////
// 管理バーの非表示(特定ユーザー以外)
//////////////////////////////////////////////////
add_filter('show_admin_bar', function () {
return current_user_can('administrator');
});
//////////////////////////////////////////////////
// ユーザー列挙対策に「REST経由の列挙」
//////////////////////////////////////////////////
add_filter('rest_endpoints', function ($endpoints) {
if (isset($endpoints['/wp/v2/users'])) {
unset($endpoints['/wp/v2/users']);
}
if (isset($endpoints['/wp/v2/users/(?P<id>[\d]+)'])) {
unset($endpoints['/wp/v2/users/(?P<id>[\d]+)']);
}
return $endpoints;
});
//////////////////////////////////////////////////
// REST APIの制限(プラグイン用は許可、一般ユーザーは拒否)
//////////////////////////////////////////////////
function deny_restapi_except_plugins( $result, $wp_rest_server, $request ) {
$namespaces = $request->get_route();
// oEmbedは許可
if( strpos( $namespaces, 'oembed/' ) === 0 ) {
return $result;
}
// Jetpackは許可
if( strpos( $namespaces, 'jetpack/' ) === 0 ) {
return $result;
}
// 投稿編集権限があるユーザーは許可
if ( current_user_can( 'edit_posts' ) ) {
return $result;
}
// それ以外はREST APIを拒否
return new WP_Error(
'rest_disabled',
__( 'The REST API on this site has been disabled.' ),
array( 'status' => rest_authorization_required_code() )
);
}
add_filter( 'rest_pre_dispatch', 'deny_restapi_except_plugins', 10, 3 );PHPコードの解説
各コードが「対応しないと、どのようなリスクがあるか」に重点をおいて解説します。
WPバージョン情報の非表示
remove_action('wp_head', 'wp_generator');PHPWordPressのバージョン情報が公開されると、既知の脆弱性を突いた攻撃を受けやすくなります。バージョン情報は隠すことで、攻撃のハードルを上げます。
ログインエラーメッセージの抑制
add_filter('login_errors', function () {
return 'ログイン情報が正しくありません。';
});PHPデフォルトでは「ユーザー名が存在しません」「パスワードが間違っています」など詳細なエラーメッセージが表示されます。これにより、攻撃者にユーザー名の存在を教えてしまいます。統一したメッセージで情報漏洩を防ぎます。
XML-RPC の無効化
add_filter('xmlrpc_enabled', '__return_false');PHPXML-RPCは外部からWordPressを操作できる機能ですが、総当たり攻撃(ブルートフォースアタック)やDDoS攻撃の標的になりやすい脆弱性があります。使用しない場合は無効化します。
REST APIエンドポイントを示すlinkタグを削除
remove_action('wp_head','rest_output_link_wp_head');
remove_action( 'template_redirect', 'rest_output_link_header', 11 );PHPREST APIのエンドポイントがheadタグに表示されると、攻撃者にAPIの存在を知らせてしまいます。必要ない場合は非表示にします。
ユーザー列挙(/?author=1 など)の無効化
if (!is_admin()) {
add_action('init', function () {
if (preg_match('/author=([0-9]*)/i', $_SERVER['QUERY_STRING'])) {
wp_redirect(home_url());
exit;
}
});
}PHP/?author=1にアクセスすると、ユーザー名が表示されます。攻撃者はこの方法でユーザー名を収集し、総当たり攻撃を仕掛けます。リダイレクトすることで、ユーザー名の列挙を防ぎます。
ヘッダーから不要な情報を削除
remove_action('wp_head', 'rsd_link');
remove_action('wp_head', 'wlwmanifest_link');
remove_action('wp_head', 'wp_shortlink_wp_head');
remove_action('wp_head', 'feed_links', 2);
remove_action('wp_head', 'feed_links_extra', 3, 0);PHP外部アプリケーション用のリンク(RSD、Windows Live Writer)、ショートリンク、RSSフィードなどを削除します。これらは現代のサイト運営ではほとんど使用されないため、削除してheadタグをシンプルにします。
コメントのコードを<code>化
add_filter('comment_text', function ($comment_text) {
return '<pre><code>' . esc_html($comment_text) . '</code></pre>';
}, 10, 1);PHPコメント欄にスクリプトタグを埋め込まれると、XSS(クロスサイトスクリプティング)攻撃を受ける可能性があります。esc_html()でエスケープし、<pre><code>で囲むことで安全に表示します。
管理バーの非表示(特定ユーザー以外)
add_filter('show_admin_bar', function () {
return current_user_can('administrator');
});PHPWordPressでは、コメント投稿や会員機能などで一般ユーザーがログインすることがあります。ログイン中は画面上部に管理バーが表示され、管理画面へのリンクやユーザー名などの情報が表示されます。管理者以外には管理バーを非表示にすることで、不要な情報の露出を防ぎます。
ユーザー列挙対策に「REST経由の列挙」
add_filter('rest_endpoints', function ($endpoints) {
if (isset($endpoints['/wp/v2/users'])) {
unset($endpoints['/wp/v2/users']);
}
if (isset($endpoints['/wp/v2/users/(?P<id>[\d]+)'])) {
unset($endpoints['/wp/v2/users/(?P<id>[\d]+)']);
}
return $endpoints;
});PHPREST API経由で/wp-json/wp/v2/usersにアクセスすると、ユーザー一覧が取得できます。攻撃者はこれを利用してユーザー名を収集します。エンドポイントを無効化して、ユーザー情報の漏洩を防ぎます。
REST APIの制限(プラグイン用は許可、一般ユーザーは拒否)
function deny_restapi_except_plugins( $result, $wp_rest_server, $request ) {
$namespaces = $request->get_route();
// oEmbedは許可
if( strpos( $namespaces, 'oembed/' ) === 0 ) {
return $result;
}
// Jetpackは許可
if( strpos( $namespaces, 'jetpack/' ) === 0 ) {
return $result;
}
// 投稿編集権限があるユーザーは許可
if ( current_user_can( 'edit_posts' ) ) {
return $result;
}
// それ以外はREST APIを拒否
return new WP_Error(
'rest_disabled',
__( 'The REST API on this site has been disabled.' ),
array( 'status' => rest_authorization_required_code() )
);
}
add_filter( 'rest_pre_dispatch', 'deny_restapi_except_plugins', 10, 3 );PHPREST APIは便利ですが、無制限に公開すると情報漏洩や攻撃の対象になります。プラグインやログインユーザーには許可し、一般ユーザーからのアクセスは拒否することで、セキュリティを強化します。
表示の確認
ログインURL変更について
今回のセキュリティ対策で一つ重要な対策を後回しにしています。それがログインURLの変更です。
最も有効なセキュリティ対策
WordPressのログインURLはデフォルトで/wp-login.phpまたは/wp-admin/です。この情報は攻撃者も知っているため、総当たり攻撃の標的になりやすい状態です。
ログインURLを変更することは、WordPressセキュリティにおいて最も有効な対策の一つです。攻撃者がログインページにたどり着けなければ、そもそも攻撃を受けるリスクが大幅に減少します。
なぜ今回は対応しないのか
今回は開発効率を優先し、ログインURL変更を後回しにしています。
理由は、Local WPの「Open Admin」機能(ワンクリックで管理画面にアクセスできる便利機能)が使えなくなるためです。開発段階では頻繁に管理画面にアクセスするため、この機能が使えないと作業効率が大きく低下します。
今後の対応
後の記事で、プラグインを使ってログインURLを変更する方法を紹介します。本番公開前には必ず実施すべき対策ですので、忘れずに対応しましょう。
まとめ
今回は、WordPressのデフォルト機能の無効化とセキュリティ対策を実装しました。
学んだこと
- デフォルト機能の無効化:不要な機能を削除してパフォーマンスを向上
- セキュリティ対策:攻撃の手がかりになる情報を隠す
- ファイルの分割管理:機能ごとにファイルを分けて管理
重要なポイント
- WordPressは拡張性・柔軟性を重視し、多くの機能をデフォルトで有効にしている
- 店舗・企業サイトでは不要な機能が多いため、無効化が必要
- 自分で読み込み順序を制御することで、パフォーマンスを最適化できる
- バージョン情報を非表示にして攻撃を防ぐ
- ユーザー列挙対策でユーザー名の漏洩を防ぐ
- REST APIを制限して情報漏洩を防ぐ
- コメント欄のXSS対策でスクリプト攻撃を防ぐ
- ログインURL変更は最も有効な対策だが、開発効率を優先し後回しにする
セキュリティの考え方
セキュリティ対策の基本は「攻撃者に情報を与えない」ことです。WordPressのバージョン、ユーザー名、APIエンドポイントなど、攻撃の手がかりになる情報を隠すことで、攻撃のハードルを上げます。
次回予告
次回は、カフェサイトの核となる「提供メニュー」を管理するためのカスタム投稿を作ります。
カフェサイトにおいて、メニュー情報の管理は最も重要な機能の一つです。カスタム投稿タイプを使うことで、コーヒー、フード、デザートなどのメニューを効率的に管理し、柔軟に表示できるようになります。
引き続き、実際に手を動かしながら進めていきましょう。
この投稿をシェアする
フッターを表示する – WordPress実践制作【WPCafe編】
PRACTICE テーマオプション取得の共通関数を作成し、カスタムフィールドからのデータ取得を効率化します。フッターのHTML構造を実装し、登録した情報をループで出力します。スタイリングではFlexboxとGridを活用したレイアウト、iframeの高さを制御する地図表示のテクニックを学びます。
- 2025/10/22
- 0comments
- 30views



