2026年9月5日
2026年9月5日
WordPressのアップロードディレクトリでPHPの実行を.htaccessで禁止する方法
wp-content/uploads に .htaccess を置いてPHPファイルへのアクセスを拒否すると、万一不正なPHPファイルがアップロードされても、ブラウザから実行される経路をふさげます。画像やPDFを置くための場所でPHPが動く理由は通常ないため、費用をかけずにできる効果の高い多層防御です。この記事では、Apache 2.4向けの設定、よく見かける「php_flag」方式が500エラーになる理由、Nginxでの書き方、動作確認と既存ファイルの点検までを説明します。
なぜuploadsでPHPを止めるのか
WordPressへの攻撃でよくある流れは、脆弱なプラグインのアップロード機能などを悪用してPHPファイル(いわゆるWebシェル)を送り込み、そのURLにアクセスして任意のコマンドを実行する、というものです。アップロードされたファイルの多くは wp-content/uploads/年/月/ に保存されるため、ここでPHPが実行できなければ、送り込まれたファイルはただのテキストとして置かれるだけになります。
ただし、これは「最後の砦」の一つにすぎません。次の点は押さえておいてください。
- アップロード自体を防ぐ対策(プラグインの更新、ファイル形式の制限)が先にある
- すでに設置されたWebシェルを消すわけではない
- uploads 以外(テーマやプラグインのフォルダ)に置かれたPHPには効かない
Apache 2.4での設定手順
作業前に、uploads フォルダに既存の .htaccess があるか確認し、あればバックアップを取ってください。プラグインによっては独自の .htaccess を置いていることがあります。
wp-content/uploads/.htaccess を作成し、次の内容を書きます。
# PHPとして実行されうる拡張子へのアクセスをすべて拒否する
<FilesMatch "\.(?i:php[0-9]?|phtml|phar|pht|phps)(\.|$)">
Require all denied
</FilesMatch>
ポイントを順に説明します。
(?i:...)で大文字小文字を区別しないため、shell.PHPのような表記ゆれにも対応しますphp[0-9]?で.php5、.php7など、サーバーによってはPHPとして扱われる拡張子も含めます- 末尾を
(\.|$)にしているのは、shell.php.jpgのような二重拡張子への対策です。ApacheのAddHandlerでPHPを関連付けているサーバーでは、ファイル名の途中にある.phpでも実行されることがあるためです Require all deniedはApache 2.4の書き方です。該当ファイルへのリクエストには403が返ります
Apache 2.2 は長くサポートが終了しているため、もし Require が使えない古い環境なら、OSやサーバーの更新自体を検討してください。
php_flag engine off を使わない理由
古い解説では、uploads の .htaccess に php_flag engine off と書く方法がよく紹介されています。この記述は mod_php(ApacheのモジュールとしてPHPを動かす方式)でしか解釈されません。現在主流の PHP-FPM や CGI 方式のサーバーでは、Apacheが知らない命令として扱われ、uploads 配下のすべてのファイル、つまり画像まで500エラーになることがあります。
PHPの動かし方に左右されない Require all denied の方が扱いやすいのはこのためです。
AllowOverrideとサーバー側設定
.htaccess が効くかどうかは、サーバー設定の AllowOverride によって決まります。許可されていないと記述が無視されたり、500エラーになったりします。共用レンタルサーバーでは通常 .htaccess が使えますが、VPSなどで自分でApacheを設定している場合は確認してください。
VPSなどで設定ファイルを編集できるなら、.htaccess ではなくバーチャルホスト設定の <Directory> に書く方が堅牢です。uploads に書き込める攻撃者は、同じ場所の .htaccess も書き換えられるかもしれないからです。
<Directory "/var/www/example.com/public/wp-content/uploads">
<FilesMatch "\.(?i:php[0-9]?|phtml|phar|pht|phps)(\.|$)">
Require all denied
</FilesMatch>
</Directory>
パスはご自身の環境に合わせて変更し、apachectl configtest で構文を確認してから再読み込みします。
Nginxでの設定
Nginx は .htaccess を読みません。uploads に .htaccess を置いても何も起きないので、サーバー設定に location を追加します。
# PHP処理用の location ~ \.php$ より前に書く
location ~* ^/wp-content/uploads/.*\.(?:php[0-9]?|phtml|phar|pht|phps)(?:\.|$) {
deny all;
}
Nginx の正規表現 location は、設定ファイルに書かれた順に評価され、最初に一致したものが使われます。PHP-FPMへ渡す location ~ \.php$ より後ろに書くと、そちらが先に一致して実行されてしまうため、順番には注意が必要です。nginx -t で確認してから再読み込みしてください。
動作確認の方法
設定したら、テスト用のPHPファイルを置いて実際に拒否されるかを確かめます。SSHが使える場合の例です。
# テストファイルを作成(中身は文字を表示するだけ)
echo '<?php echo "executed";' > wp-content/uploads/qa-exec-test.php
# 403 が返れば成功。200 で "executed" が表示されたら設定が効いていない
curl -s -o /dev/null -w "%{http_code}\n" https://example.com/wp-content/uploads/qa-exec-test.php
# 確認が終わったらテストファイルを削除する
rm wp-content/uploads/qa-exec-test.php
あわせて、通常の画像URLがこれまでどおり表示されること、メディアライブラリでのアップロードやサムネイル生成が動くことも確認してください。キャッシュ系やセキュリティ系のプラグインの中には uploads 配下に独自の仕組みを持つものがあるため、プラグインの動作もひととおり見ておくと安心です。
既存のPHPファイルを点検する
設定を入れる前から不正なファイルが置かれている可能性もあります。uploads 配下にPHPファイルが無いかを一度確認しておきましょう。
find wp-content/uploads -type f -iname "*.php*" -o -type f -iname "*.phtml" -o -type f -iname "*.phar"
見つかったファイルがすべて不正というわけではありません。一部のプラグインは自分のフォルダに中身のない index.php を置いて、フォルダ一覧の表示を防いでいます。判断がつかないファイルは、いきなり消さずにサイト全体のバックアップを取り、ファイルの中身、作成日時、どのプラグインのフォルダにあるかを確認してください。eval、base64_decode、assert といった処理が難読化されて並んでいる場合は、改ざんを疑ってマルウェア対策の手順に進むべきサインです。
プラグインで設定する選択肢
.htaccess を直接編集するのに不安がある場合は、セキュリティ系プラグインの機能を使う方法もあります。たとえば Solid Security や Sucuri Security には、uploads でのPHP実行を止める「強化(ハードニング)」系の設定が用意されています。項目名や配置はバージョンで変わるため(2026年10月現在)、各プラグインの説明を確認してから有効にしてください。
プラグインが書き込む内容も、結局は上で説明した .htaccess の記述とほぼ同じです。Nginx 環境ではプラグインからは設定できないことが多い点にも注意してください。
うまくいかないときの確認ポイント
- 画像まで表示されなくなった:
php_flagの行が残っていないか、FilesMatch の正規表現が広すぎないかを確認します - テストファイルが200で実行される: Nginx環境で .htaccess を置いているだけ、または
AllowOverride Noneで無視されている可能性があります - サイト全体が500エラーになった: .htaccess の記述ミスです。FTPなどで該当ファイルの名前を変えれば元に戻せます
まとめ
- uploads でのPHP実行禁止は、Webシェルを送り込まれた場合の被害を抑える多層防御の一つ
- Apache 2.4 では
<FilesMatch>とRequire all deniedを使い、二重拡張子や大文字表記も対象にする php_flag engine offは PHP-FPM 環境で500エラーの原因になるため避ける- Nginx は .htaccess を読まないので、PHP処理より前に deny の location を書く
- 設定後はテストファイルで403を確認し、既存のPHPファイルも点検する