消したあとに、画像が表示されなくなっていた
使っていないはずの画像を数百ファイルまとめて消したら、記事の画像が出なくなりました。整理していたのは、記事の画像として保存していたファイルです。消す前には、消すファイルを tar コマンドで 1 つのファイルに固めて、バックアップを作っています。
ところが、消したあとに記事を開き直して見ていくと、8 本の記事で画像が表示されなくなっていました(リンク切れ)。表示されなくなっていたのは、合わせて 35 か所です。どれも「使っていない」と判断した画像でした。
名前で探していたので、見つからなかった
「使っていない」の判断は、メディア(添付ファイル)に登録されている名前で記事を探して決めていました。登録の名前が記事の中に出てこなければ使っていない、という考え方です。
ところが、画像が表示されなくなっていた記事では、その画像をページビルダーの画像の部品で、リンクのない画像として使っていました。ページビルダーのデータは記事の本文ではなく、postmeta(記事に付く追加のデータ)に入ることが多いので、本文を探すだけでは出てきません。しかも、記事の側で使っていたファイル名が、メディアに登録されている名前と頭の 1 文字だけ違っていました。保存したときに、ファイル名が少し変わっていたためです。
この 2 つが重なって、名前で探しても見つからず、「使っていない」と見誤っていました。登録の名前と、実際に使われているファイルのパスが同じとは限らない、というのが今回気づいたことです。
バックアップから、消してしまったファイルだけを戻す
消す前に tar で固めたバックアップがあったので、そこから消してしまった 35 ファイルだけを戻しました。全部を戻すと、本当に使っていない画像まで戻ってしまうので、戻すファイルの一覧を作って、その分だけを取り出しています。
wp-content/uploads/2024/05/sample-image-01.jpg wp-content/uploads/2024/05/sample-image-01-300x300.jpg wp-content/uploads/2024/06/sample-image-02.jpg
tar -tzf backup-images.tar.gz | head tar -xzf backup-images.tar.gz -T restore-list.txt
| 書き方 | 役目 |
|---|---|
tar -tzf |
取り出さずに、中に入っているファイルの名前を出す。一覧に書くパスの形(先頭が wp-content/ からか、など)をここで合わせる |
tar -xzf |
gzip で固めた tar からファイルを取り出す |
-T restore-list.txt |
一覧のファイルに書いたものだけを取り出す |
一覧に書くパスは、tar で固めたときのパスと同じ形にしておく必要があります。形が違うと、そのファイルは取り出されません。戻したあとは、すべての記事を開き直し、表示されない画像が 0 になったことを確認しました。
消す前の確認のしかたを変えた
このことがあってから、消す前の確認のしかたを変えました。メディアの登録の名前ではなく、実際に消すファイルのパスから「年/月/ファイル名」の形の文字を作り、それが DB のどこかに出てこないかを全文で探します。探す場所は、記事の本文(wp_posts の post_content)、ページビルダーのデータなどが入る wp_postmeta の meta_value、テーマやウィジェットの設定が入る wp_options の option_value の 3 つです。
SELECT ID, post_type, post_status
FROM wp_posts
WHERE post_content LIKE '%2024/05/sample-image-01.jpg%';
SELECT post_id, meta_key
FROM wp_postmeta
WHERE REPLACE(meta_value, '\\/', '/') LIKE '%2024/05/sample-image-01.jpg%'
AND meta_key NOT IN ('_wp_attached_file', '_wp_attachment_metadata');
SELECT option_name
FROM wp_options
WHERE REPLACE(option_value, '\\/', '/') LIKE '%2024/05/sample-image-01.jpg%';
| 書き方 | 役目 |
|---|---|
LIKE '%2024/05/sample-image-01.jpg%' |
「年/月/ファイル名」がどこかに入っていれば当たる。URL の頭(ドメイン)は含めないので、http と https の違いや作業環境の名前に左右されない |
REPLACE(meta_value, '\\/', '/') |
ページビルダーなどが内容を JSON で保存すると、/ が \/ の形で入ることがある。それを / に戻してから比べる |
meta_key NOT IN ('_wp_attached_file', '_wp_attachment_metadata') |
メディアに登録したときに WordPress が作る、その画像自身のデータは除く。除かないと、どの画像も「参照がある」になってしまう |
wp_postmeta・wp_options |
本文には出てこない使い方(ページビルダーの部品、テーマの設定など)も探す |
日本語などのファイル名は、URL の中で %E7%94%BB... のような形(URL エンコード)になって保存されていることがあります。SQL の LIKE だけでは両方の形を探しにくいので、DB から取り出した文字を URL のデコードで元に戻してから比べるようにしました。サイズ違いのファイル(-300x300 などが付いたもの)も、1 つずつ同じように探します。
消してよいのは、3 つのどこにも参照が 0 のファイルだけです。また、消したあとに記事を開き直して表示されない画像がないことを確認するまでは、バックアップは消さずに残しておきます。
確認した結果
記事の SQL が流れて件数が出るかを、作業環境の DB で確認しました。読むだけの SELECT で、DB は書き換えていません。今回検証したサイトの作業環境では、ある画像のファイル名の頭の部分(「2025/01/ファイル名の頭」)で探して、次の件数が出ました。
| 探した場所 | 件数 |
|---|---|
wp_posts の post_content |
0 |
wp_postmeta の meta_value(REPLACE で戻して比べる・meta_key で除かない) |
2 |
wp_postmeta の meta_value(_wp_attached_file と _wp_attachment_metadata を除く) |
0 |
wp_options の option_value(REPLACE で戻して比べる) |
0 |
postmeta で当たった 2 件は、どちらもその画像自身の登録のデータ(_wp_attached_file と _wp_attachment_metadata)でした。この 2 つを除くと 0 件になるので、この画像はどこからも使われていない、と判断できます。除かずに探すと、どの画像にも参照があるように見えてしまうので、SQL に除く条件を入れています。
なお、この作業環境の DB では、この画像の参照が \/ の形で入っている例はなかったので、REPLACE で戻す部分が効く場面はここでは試せていません。tar の -T で一覧のファイルだけを取り出す動きは、作業環境で試しのファイルを固めて取り出し、一覧に書いたファイルだけが戻ることを確認しました。
気をつけること
次に消すときは、ここだけ先に見ます。
- 消す前に、本文・postmeta・options の 3 か所の SQL で参照が 0 かを見る(本文だけを探さない)
- 消したあとは記事を開き直し、表示されない画像が 0 になるまでバックアップを残す
まとめ
画像を消す前に、「年/月/ファイル名」の形で本文・postmeta・options の 3 か所を全文で探し、参照が 0 のものだけを消します。
