作業環境にない画像だけ本番から読む:uploads の .htaccess に 302 の転送を足す

広告WordPress・PHP を学べる本

Amazonのアソシエイトとして、WordPress functions.phpは適格販売により収入を得ています。

DB だけコピーし直すと、画像が表示されなくなる

管理しているサイトで、本番の DB を作業環境にコピーし直して作業していたときのことです。記事や固定ページの中身は新しくなったのですが、最近本番で上げた画像だけが表示されず、ところどころ画像が表示されない状態になっていました。DB には新しい画像のデータが入っているのに、作業環境の wp-content/uploads には、そのファイルがまだないためです。

uploads のフォルダを毎回まるごとコピーすれば揃いますが、画像が多いサイトでは時間も容量もかかります。作業環境で確かめたいのはたいていテーマやプラグインの動きなので、画像そのものは本番にあるものを見られれば十分です。そこで、作業環境にない画像だけを、本番の同じ URL から読むようにしました。

uploads の .htaccess に足す設定

作業環境の wp-content/uploads/.htaccess の最後に、次の設定を足します。サイトの名前は例として、作業環境を example.local、本番を example.com にしています。

wp-content/uploads/.htaccess(作業環境だけ・最後に足す)
# BEGIN local-fallback
<IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteCond %{HTTP_HOST} ^example\.local$ [NC]
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule ^(.*)$ https://example.com/wp-content/uploads/$1 [R=302,L]
</IfModule>
# END local-fallback
書き方 役目
RewriteCond %{HTTP_HOST} ^example\.local$ 作業環境の名前で開いたときだけ転送する。間違って本番に置いても、条件に合わないので何もしない
RewriteCond %{REQUEST_FILENAME} !-f 作業環境にファイルがあるときは転送せず、作業環境のファイルをそのまま出す
RewriteRule ^(.*)$ https://example.com/wp-content/uploads/$1 uploads から下のパスを、そのまま本番の uploads の URL につなげる
[R=302,L] 一時的な転送にする。ずっと本番へ移すわけではないので 301 にはしない

HTTP_HOST の条件を入れているのは、この .htaccess を本番にうっかり上げてしまったときのためです。条件がないと、本番でファイルがないときに本番の同じ URL へ転送し、そこでもファイルがないのでまた転送する、というくり返しになります。作業環境の名前を条件にしておけば、本番では 1 行も動かず、ふつうの 404 になります。

プラグインの区切りの外に書く

uploads の .htaccess は、プラグインが書いていることがあります。今回検証したサイトでは、画像を WebP に変える Converter for Media が、# BEGIN Converter for Media から # END Converter for Media までの区切りに自分の設定を書いていました。プラグインは設定を保存し直すたびにこの区切りの中を書き直すので、中に足した行は消えてしまいます。

そのため、足す設定はプラグインの区切りの外、ファイルの最後に、自分の # BEGIN local-fallback〜# END local-fallback の区切りで書きました。こうしておくと、プラグインが自分の区切りを書き直しても、こちらの設定はそのまま残ります。

Converter for Media の設定は、元の画像のファイルがあるとき(REQUEST_FILENAME が -f のとき)だけ WebP や AVIF のファイルを出す形になっています。作業環境にない画像はこの条件に合わないので、プラグインの設定を通り抜けて、最後の転送の設定に届きます。

確かめた結果

管理しているサイトの作業環境にこの設定を入れ、curl で応答の見出しを見て確かめました。本番の画像の URL を開くのではなく、作業環境の応答の Location の見出しで、どこへ転送しようとしているかを見ています。

応答の見出しだけを見る(例)
curl -s -o /dev/null -D - "http://example.local/wp-content/uploads/2025/01/sample.png"
確かめたこと 結果
作業環境にある画像 200 で、作業環境のファイルが返った
作業環境にある画像(Accept: image/webp を付けて) 200 で、Content-Type: image/webp が返った(Converter for Media の設定も今までどおり動いている)
作業環境にない画像 302 で、Location が本番の同じパスの URL になった
作業環境にない画像(Accept: image/webp を付けて) 同じく 302 で、本番の同じパスの URL へ
手元の画像を 1 つ一時的に隠して、ブラウザで開く 本番の画像が表示された

URL に ?nc=123 のようなクエリを付けて開いたときは、転送先の URL にもそのクエリがそのまま付いていました。どれも作業環境だけで確かめていて、本番の設定は変えていません。

WordPress の外のフォルダにも使える

同じ形は、WordPress の uploads 以外のフォルダにも使えます。たとえば、写真をまとめて置いているフォルダが本番にだけあるときは、そのフォルダの .htaccess に同じ設定を書き、RewriteRule の転送先を本番のそのフォルダの URL に変えます。パスを $1 でそのままつなげるので、フォルダの中の階層が本番と同じであれば、ほかに変えるところはありません。

気をつけること

作業環境で、画像を消す作業(使っていない画像の整理など)を確かめるときは、この設定をいったん外します。設定が入ったままだと、作業環境でファイルを消しても本番の画像が表示されるので、消えたかどうかを画面で見分けられません。

また、本番の uploads に画像のリンク対策(Referer を見て、ほかのサイトからの読み込みを止める設定)が入っていると、作業環境から本番の画像を読めないことがあります。作業環境のページから読むと、Referer が作業環境の名前になるためです。作業環境の .htaccess に作業環境の名前を許す設定(SetEnvIf Host)を足すと、作業環境にある画像は見えるようになりますが、本番へ転送した画像は本番側の設定で止まります。リンク対策が入っているサイトでは、この形だけでは本番の画像が出ないことがある、と考えておくのが安全です。

  • この設定は作業環境だけに置く。git などで本番へ上げるファイルに入る場合は、本番に上がらないようにしておく
  • プラグインが書く # BEGIN〜# END の区切りの中には書かない
  • 画像を消す作業を確かめるときは、いったん外す
  • 本番に画像のリンク対策があるときは、作業環境から本番の画像が読めないことがある

まとめ

  • 本番の DB だけをコピーし直すと、新しい画像が作業環境の uploads になく、表示が切れる
  • uploads の .htaccess に、ファイルがないときだけ本番の同じ URL へ 302 で転送する設定を足すと、uploads を毎回コピーしなくて済む
  • HTTP_HOST を条件にして、作業環境の名前で開いたときだけ動くようにする
  • プラグインの区切りの外に、自分の区切りで書くと、プラグインが書き直しても消えない
  • 画像を消す作業を確かめるときと、本番にリンク対策があるときは注意する

参考

SiteBeacon(サイトビーコン)

サイトが止まったら、すぐに気づける。

ホームページを 5 分ごとにチェックして、止まったとき・エラーが出たとき・SSL やドメインの期限が近いときに、メールでお知らせします。2 サイトまでは、ずっと無料で使えます。

SiteBeacon を見る サイトを直す相談をする

監視で気づいたあとの直す作業も、同じ担当がお受けします。