CentOS Stream 9 の Apache で、サイトごとの設定に WordPress 共通の制限と許可する拡張子を書く(サーバー管理者向け)
篠原 隆司
アフィリエイト広告を利用しています
このページの内容が役に立ったら X (旧twitter) でフォローして頂けると励みになります
挨拶や報告は無しで大丈夫です
さくらのVPSのCentOS Stream 9で、サイトごとの Apache の設定(VirtualHost)に、WordPress 共通の制限と、そのサイトだけ許可する拡張子を書いた覚書です。
前の記事で、サーバー全体の設定(許可する拡張子と、探りを fail2ban で遮断する準備)をしました。今回は、サーバー管理者の立場で、サイトごとの設定を書きます。
サイトの担当者が .htaccess で行うことは、次の記事で扱います。
記事の中の IP アドレス・ドメインなどは、実際のものから書き換えています。
なお本番環境で利用される場合はここにある内容だけを鵜呑みにせずセキュリティ専門家に相談されることをお勧めします。
環境
| 実施日 | 2026-11-09 |
| サーバ | さくらのVPS 2G
|
| OS | CentOS Stream 9 |
| SELinux | 無効(Disabled) |
cat /etc/redhat-release
CentOS Stream release 9
経緯と解決策
経緯について
WordPress のサイトには、.env のような探りのほかに、WordPress を狙った探りも届きます。wp-config.php の中身、アップロードの置き場(wp-content/uploads/)に置かれたかもしれない .php、使われていない xmlrpc.php などです。
これらは、どの WordPress のサイトでも同じです。サイトごとの .htaccess に書くと、サイトの数だけ同じ設定が並び、直すときに漏れが出ます。
また、前の記事の許可する拡張子は、サーバー全体の既定です。ダウンロードのファイル(zip など)を置くサイトや、Ruby・Perl などを動かすサイトでは、そのサイトだけ足す必要があります。
解決案を考える
サイトごとの設定(VirtualHost)に、次の 3 つを書きます。サーバー管理者が書くところです。
- WordPress 共通の制限:1 つのファイルにまとめ、WordPress のサイトから
Includeで読みます。直すのは 1 か所で済みます - そのサイトだけ許可する拡張子:目印
ext_allowを立てて、サーバー全体の既定に足します - エラーページ:拒んだときに出すページを、サイトの既定として決めます
VirtualHost の書き換え(RewriteRule)は、%2e などを元の文字に戻したあとの URL を見て、.htaccess より前に動きます。WordPress まで届かないので、PHP も動きません。
事前確認
設定を書く前に、サーバー全体の設定と、サイトごとの設定のファイルを確かめます。
作業は root で行います。例のサイトは example.com で、サイトごとの設定は /etc/httpd/conf.d/vhosts/example.com.conf にあるとします。
サーバー全体の設定が入っているか
前の記事の 2 つのファイルがあり、許可する拡張子の並びができているかを見ます。
ls /etc/httpd/conf.d/allow-ext.conf /etc/httpd/conf.d/fail2ban-web.conf
httpd -t -D DUMP_RUN_CFG | grep WEB_ALLOW_EXT
2 つのファイルが並び、Define: WEB_ALLOW_EXT=php|html|… の行が出れば、入っています。
サイトごとの設定の、443 の VirtualHost
書き足すのは、https の VirtualHost(<VirtualHost *:443>)の中です。http(80)を https へ転送しているなら、80 の VirtualHost には書かなくてかまいません。
grep -n -e 'VirtualHost' -e 'Redirect' /etc/httpd/conf.d/vhosts/example.com.conf
今回行うこと
はじめに、WordPress 共通の制限を 1 つのファイルに作ります。
そのあと、サイトごとの設定に、そのファイルの読み込みと、エラーページと、そのサイトだけ許可する拡張子を書いて反映します。
最後に、試しのアクセスで、拒めることと、サイトが今までどおり動くことを確かめます。
WordPress 共通の制限を作る
/etc/httpd/conf.d/include/ を作り、wordpress.conf を置きます。
mkdir -p /etc/httpd/conf.d/include
vi /etc/httpd/conf.d/include/wordpress.conf
###############################################################################
# WordPress 共通の制限
#
# 置き場:/etc/httpd/conf.d/include/wordpress.conf
# 自動では読み込まれない(conf.d/*.conf も conf.d/vhosts/*.conf も、このディレクトリまでは見ない)。
# WordPress を使っているサイトのファイル(conf.d/vhosts/<ドメイン>.conf)の、443 の VirtualHost の中で読み込む:
# Include conf.d/include/wordpress.conf
#
# 拒むときに、fail2ban 向けの目印を付ける(中:f2b_mid、弱:f2b_weak)。目印の付いたアクセスは、
# サーバー全体の設定(conf.d/fail2ban-web.conf の GlobalLog)が fail2ban 向けのファイルに書く
# VirtualHost の書き換えは、URL を元の文字に戻したあと、.htaccess や Require より前に動く。
# サブディレクトリに置いた WordPress にも効くよう、URL のどこにあっても拾う
# サイトごとに違うもの(エラーページ、許可する拡張子を足すもの、開いてよいサイト独自の .php など)は、
# サイトのファイルや .htaccess に書く
###############################################################################
RewriteEngine On
# 強(.env など)と重なったときは、サーバー全体の設定(fail2ban-web.conf の GlobalLog)が強だけに書く
# 中:wp-config.php(PHP が止まったときに、中身を見せない)
RewriteRule /wp-config\.php(?:/|$) - [E=f2b_mid:1,F]
# 中:WordPress が中から読み込む部品
RewriteRule /wp-admin/includes/ - [E=f2b_mid:1,F]
# 中:アップロードの置き場の PHP
RewriteRule /wp-content/uploads/.*\.php(?:/|$) - [E=f2b_mid:1,F]
# 弱:xmlrpc.php(使うサイト(Jetpack など)は、そのサイトのファイルで、この Include より前に飛ばす)
RewriteRule /xmlrpc\.php(?:/|$) - [E=f2b_weak:1,F]
# wp-includes の PHP は、WordPress が中から読み込む部品なので直接は開かせない(404 にして、ファイルがあることも知らせない。数えない)
# 旧エディター(Classic Editor)がブラウザから呼ぶ wp-tinymce.php だけは除く
RewriteCond %{REQUEST_URI} !/wp-includes/js/tinymce/wp-tinymce\.php$
RewriteRule /wp-includes/.*\.php(?:/|$) - [R=404,L]
- conf.d/include/:
conf.d/*.confも、サイトごとの設定のconf.d/vhosts/*.confも、その下のディレクトリまでは読みません。WordPress のサイトだけがIncludeで読むように、下のディレクトリに置きます - [E=f2b_mid:1,F]:目印
f2b_midを付けて、403 で拒みます。目印の付いたアクセスは、サーバー全体の設定が fail2ban 向けのファイルに書きます - URL の頭に ^ を付けない:
/blog/wp-config.phpのように、サブディレクトリに置いた WordPress にも当てるためです - (?:/|$):
/wp-config.php/xのように、後ろに続けた URL も拾います - xmlrpc.php:外から投稿・操作するための古い仕組みです。今は REST API とアプリケーションパスワードに移っていて、ほとんどのサイトでは要りません。そのため既定では拒み、弱に数えます。Jetpack など、今も使うものを入れているサイトだけ、サイトごとの設定で通します(下の「サイトごとの設定に書く」)
- wp-includes の .php:拒むのではなく 404 にして、ファイルがあることも知らせません。fail2ban には数えません
サイトごとの設定に書く
控えを取ってから、サイトごとの設定を開きます。
cp -p /etc/httpd/conf.d/vhosts/example.com.conf /etc/httpd/conf.d/vhosts/example.com.conf.20261109
vi /etc/httpd/conf.d/vhosts/example.com.conf
控えの名前は、.conf で終わらないようにします。conf.d/vhosts/*.conf が読み込まれる場合、.conf で終わる控えも読み込まれてしまうためです。
<VirtualHost *:443> の中の終わりのほう(</VirtualHost> の前)に、次を足します。
# エラーページ(このサイトの既定。サイトの担当者は .htaccess で同じ番号を書けば上書きできる)
ErrorDocument 403 /error-pages/403.html
ErrorDocument 404 /error-pages/404.html
ErrorDocument 500 /error-pages/500.html
# このサイトだけの例外(使わないなら書かない。使うときは行頭の # を消す)
#RewriteEngine On
# 許可する拡張子を足す(サーバー全体の既定 allow-ext.conf に足す)
#RewriteRule "\.(?:zip|csv|xlsx)$" - [NC,E=ext_allow:1]
# xmlrpc.php を使う(Jetpack など)。下の WordPress 共通の制限を飛ばす
#RewriteRule /xmlrpc\.php$ - [L]
# WordPress 共通の制限
Include conf.d/include/wordpress.conf
- ErrorDocument:拒んだときなどに出すページです。並びの拡張子(例は
html)は、許可する拡張子に入っているものにします。並びに無い拡張子のページは、そのページ自体が拒まれます。
サーバー全体で、歓迎しない botや海外からのアクセスをRequireで拒んでいるときは、エラーページを出すための中の転送まで拒まれ、Apache の素のページになることがあります。エラーページを通す 1 行は、それぞれの記事の設定に入れてあります - [NC,E=ext_allow:1]:当てはまったアクセスに目印
ext_allowを立てます。サーバー全体の許可する拡張子は、この目印があれば拒みません。NCで、大文字と小文字を区別しません - Ruby・Perl などを、このサイトだけで動かす:その拡張子(
rb・pl・cgiなど)を、同じ形で足します。ext_allowが立ったアクセスは、サーバー全体の探りの中の並びでも拒みません。強(.で始まるもの・鍵・DB のダンプなど)は、ext_allowがあっても拒みます - 既定を使わず、このサイトで決める:
RewriteRule ^ - [E=ext_allow:1]と書くと、このサイトでは、許可する拡張子と探りの中の並びが効かなくなります。拒むものは、このサイトの設定に自分で書きます - RewriteRule /xmlrpc\.php$ - [L]:
xmlrpc.phpへのアクセスで、ここから後ろの書き換えを止めます。後ろにある WordPress 共通の制限が当たらなくなり、このサイトだけxmlrpc.phpが通ります。サイトの.htaccessで開いてよい.phpを決めているときは、そちらにもxmlrpc.phpを足してもらいます(次の記事) - 置く順:
ext_allowの行とxmlrpc.phpの行は、Includeより前に置きます。書き換えは書いた順に動き、[L]で止まる規則より後ろにあると、目印が立たないためです。立たなかったときは、拒む側に倒れます(足した拡張子が 403 になる)
反映する
apachectl configtest && systemctl reload httpd
Syntax OK
サイトの .htaccess に、同じ制限(wp-config.php や xmlrpc.php を拒む設定)が書いてあっても、そのままでかまいません。VirtualHost の書き換えが先に拒むので、.htaccess の分には届きません。
動きを確かめる
サーバーから、試しのアクセスを送ります。
UA='Mozilla/5.0 (probe test)'
for p in /wp-config.php /wp-config%2ephp /wp-admin/includes/f2b-test.php /wp-content/uploads/f2b-test.php /xmlrpc.php /wp-includes/f2b-test.php /wp-includes/js/tinymce/wp-tinymce.php /; do
printf '%-42s ' "$p"; curl -s --path-as-is -A "$UA" -o /dev/null -w '%{http_code}\n' "https://example.com$p"
done
tail -n 4 /var/log/httpd/fail2ban-mid_log
tail -n 1 /var/log/httpd/fail2ban-weak_log
/wp-config.php 403
/wp-config%2ephp 403
/wp-admin/includes/f2b-test.php 403
/wp-content/uploads/f2b-test.php 403
/xmlrpc.php 403
/wp-includes/f2b-test.php 404
/wp-includes/js/tinymce/wp-tinymce.php 200
/ 200
192.0.2.10 [09/Nov/2026:00:53:30 +0900] example.com "GET /wp-config.php HTTP/1.1" 403
192.0.2.10 [09/Nov/2026:00:53:30 +0900] example.com "GET /wp-config%2ephp HTTP/1.1" 403
192.0.2.10 [09/Nov/2026:00:53:30 +0900] example.com "GET /wp-admin/includes/f2b-test.php HTTP/1.1" 403
192.0.2.10 [09/Nov/2026:00:53:30 +0900] example.com "GET /wp-content/uploads/f2b-test.php HTTP/1.1" 403
192.0.2.10 [09/Nov/2026:00:53:30 +0900] example.com "GET /xmlrpc.php HTTP/1.1" 403
- 中:
wp-config.php・wp-admin/includes/・uploadsの.phpが 403 で拒まれ、中のファイルに書かれます。/wp-config%2ephpも、%2eを戻したあとの URL で拾われます - 弱:
/xmlrpc.phpは、弱のファイルに書かれます - wp-includes の .php:404 で、どのファイルにも書かれません。
wp-tinymce.phpは、これまでどおり返ります - 192.0.2.10:サーバー自身なので、fail2ban は数えず、遮断もしません
あわせて、ブラウザで、トップページ、記事のページ、管理画面へのログイン、投稿の編集と保存、メディアの追加が、今までどおり行えるかを見ます。
そのサイトだけ許可する拡張子を足したときは、それも確かめます。例として、zip と cgi を足しました。
RewriteEngine On
RewriteRule "\.(?:zip|cgi)$" - [NC,E=ext_allow:1]
UA='Mozilla/5.0 (probe test)'
for p in /f2b-test.zip /F2B-TEST.ZIP /f2b-test.cgi /f2b-test.csv /f2b-test.sql; do
printf '%-16s ' "$p"; curl -s -A "$UA" -o /dev/null -w '%{http_code}\n' "https://example.com$p"
done
tail -n 1 /var/log/httpd/fail2ban-strong_log
/f2b-test.zip 404
/F2B-TEST.ZIP 404
/f2b-test.cgi 404
/f2b-test.csv 403
/f2b-test.sql 403
192.0.2.10 [09/Nov/2026:00:58:41 +0900] example.com "GET /f2b-test.sql HTTP/1.1" 403
- zip・cgi:このサイトでは拒まれず、ファイルを探しに行きます(無いファイルなので 404)。
cgiは、サーバー全体の探りの中の並びにも入っていますが、拒まれず、中のファイルにも書かれません。大文字のZIPも同じです - csv:足していないので、サーバー全体の既定のとおり 403 で拒まれます。どのファイルにも書かれません
- sql:強なので、
ext_allowがあっても 403 で拒まれ、強のファイルに書かれます
xmlrpc.php を通すサイトでは、飛ばす 1 行を書いたあとに確かめます。
次の例は、次の記事の .htaccess(開いてよい .php を決めるもの)を、すでに入れているサイトで確かめたものです。
UA='Mozilla/5.0 (probe test)'
curl -s -A "$UA" -o /dev/null -w '%{http_code}\n' https://example.com/xmlrpc.php
tail -n 1 /var/log/httpd/fail2ban-mid_log
tail -n 1 /var/log/httpd/fail2ban-weak_log
403
192.0.2.10 [09/Nov/2026:01:24:08 +0900] example.com "GET /xmlrpc.php HTTP/1.1" 403
192.0.2.10 [09/Nov/2026:01:21:37 +0900] example.com "GET /xmlrpc.php HTTP/1.1" 403
この例では、飛ばす 1 行を書く前(01:21)は弱のファイルに、書いたあと(01:24)は中のファイルに書かれています。WordPress 共通の制限は飛ばせていますが、その .htaccess が、開いてよい .php の並びに無い xmlrpc.php を中で拒んでいます。xmlrpc.php を通すには、サイトの担当者に、.htaccess の並びにも足してもらいます(次の記事)。足したあとと、.htaccess で開いてよい .php を決めていないサイトでは、xmlrpc.php が WordPress まで届き、ブラウザで開くと「POST のアクセスだけを受け付ける」という返事になります。
遮断を解く
解き方は、前の記事の「遮断を解く」です。
サイトの利用者が遮断されたと分かったら、その人が開いた URL が、WordPress 共通の制限に当たっていないかを確かめます。
元に戻す
サイトごとの設定から、足した行を消して反映します。WordPress 共通の制限をどのサイトでも使わなくなったら、ファイルも消します。
# 控えに戻す(控えを取ったあとに直したところがあれば、その分も戻るので気をつける)
cp -p /etc/httpd/conf.d/vhosts/example.com.conf.20261109 /etc/httpd/conf.d/vhosts/example.com.conf
apachectl configtest && systemctl reload httpd
# どのサイトも読まなくなったら
grep -l 'include/wordpress.conf' /etc/httpd/conf.d/vhosts/*.conf
# 何も出なければ、どのサイトも読んでいないので消す
rm /etc/httpd/conf.d/include/wordpress.conf
補足説明
VirtualHost と .htaccess のどちらに書くか
| VirtualHost | .htaccess | |
|---|---|---|
| 書く人 | サーバー管理者 | サイトの担当者 |
| 動く時点 | URL を元の文字に戻したあと、Require や .htaccess より前 | サーバー全体の <If> や Require より後 |
| 反映 | systemctl reload httpd が要る | 保存した時点で効く |
| 向くもの | どのサイトでも同じ制限、許可する拡張子を足す | サイトごとに違う並び(開いてよい .php)、許可する拡張子を外す |
許可する拡張子を足す ext_allow の目印は、サーバー全体の <If> より前に立てる必要があります。.htaccess の書き換えは <If> より後に動くので、間に合いません。足すのは VirtualHost だけでできます。
エラーページの上書き
VirtualHost の ErrorDocument はサイトの既定です。サイトの担当者が .htaccess に同じ番号の ErrorDocument を書けば、そちらが使われます。
ただし、VirtualHost の書き換えで拒んだアクセスは .htaccess まで進まないので、そのときは VirtualHost のページが出ます。
WordPress ではないサイト
WordPress 共通の制限は、WordPress のサイトだけが Include で読みます。ほかのアプリ(Laravel など)のサイトでは、同じ考え方で、そのアプリ向けのファイルを conf.d/include/ に作り、そのアプリのサイトから読みます。
まとめ
- どの WordPress でも同じ制限は、
conf.d/include/wordpress.confにまとめ、WordPress のサイトの VirtualHost からIncludeで読む - 拒むときに、fail2ban 向けの目印(中・弱)を付ける。
wp-includesの.phpは 404 にして数えない - そのサイトだけ許可する拡張子は、VirtualHost で目印
ext_allowを立てて足す。.htaccessでは間に合わない - エラーページは VirtualHost でサイトの既定にし、サイトの担当者は
.htaccessで上書きできる xmlrpc.phpは既定で拒み、Jetpack など今も使うものがあるサイトだけ、VirtualHost で共通の制限を飛ばす
xmlrpc.php を使うサイト(Jetpack など)があっても、WordPress 共通の制限からは外さず、そのサイトの設定で飛ばしてください。共通のファイルを直すと、読み込んでいるすべてのサイトに効きます。サイトの担当者が .htaccess で行うことは、次の記事で扱います。
このページの内容が役に立ったら X (旧twitter) でフォローして頂けると励みになります
本ページの内容は可能な限り正確な情報を提供するよう努めていますが、内容の正確性・最新性・安全性を保証するものではありません。本情報を利用して生じたいかなる損害についても、当方は一切の責任を負いません。実施にあたっては必ずご自身の判断と自己責任にてお願いいたします。