WordPress で使っていない大きさの画像を探して安全に消す(これから作らせない設定つき)

概要説明

WordPress が自動で作る縮小版の画像のうち、使っていない大きさを WP-CLI で数えて見つけ、画像の情報(メタデータ)と WebP に変換したファイルも合わせて安全に消す方法。intermediate_image_sizes_advanced でこれから作らせない設定と、約 690MB 空いた事例つき。

WordPress は、画像を 1 枚アップロードするたびに、大きさの違う画像(縮小版)を何枚も作ります。テーマが使う大きさも足されるので、使っていない大きさのファイルが、気づかないうちにたまっていきます。

自分で運営しているゲームの攻略サイト(画像 約 1,000 枚)で数えてみると、元の画像 610MB に対して、縮小版が約 2.1GB ありました。そのうち、どこにも使っていない大きさが約 600MB(WebP に変換したファイルを合わせると約 690MB)です。

この記事では、使っていない大きさの見つけ方、安全な消し方、これから作らせない方法を、実際に行った手順でまとめます。

WordPress が作る画像の大きさ

画像を 1 枚アップロードすると、次の大きさのファイルが作られます。

大きさの名前 大きさ 決まる所・使い道
thumbnail 150px 設定 → メディア で変えられる
medium 300px 設定 → メディア で変えられる
large 1024px 設定 → メディア で変えられる
medium_large 幅 768px 設定の画面には出ない。srcset(画面の幅に合わせて画像を選ぶ仕組み)の候補
1536×1536 1536px WordPress 5.3 から。大きい画面向けの srcset の候補
2048×2048 2048px WordPress 5.3 から。大きい画面向けの srcset の候補
テーマが足す大きさ テーマによる add_image_size で登録されたもの(例:size1・size2・size3・size-card)

1536×1536・2048×2048 は、元の画像がそれより大きいときだけ作られます。

全部を合わせると、1 枚の画像から 10 枚近いファイルができることもあります。

手順 1:大きさごとに、ファイルの数と容量を数える

WP-CLI(サーバーで WordPress を操作するコマンド)の wp eval-file で、次のファイルを動かします。読むだけで、何も消しません。エックスサーバーなど、SSH で WP-CLI が使えるサーバーを前提にしています。

count-image-sizes.php
<?php
// 画像の大きさごとに、ファイルの数と容量を数える(読むだけ・何も消さない)
// 使い方(WordPress のフォルダで): wp eval-file count-image-sizes.php
if ( ! defined( 'WP_CLI' ) || ! WP_CLI ) {
	exit; // ブラウザから開かれても何もしない
}

$ids = get_posts(
	array(
		'post_type'      => 'attachment',
		'post_mime_type' => 'image',
		'post_status'    => 'inherit',
		'posts_per_page' => -1,
		'fields'         => 'ids',
	)
);

$stat = array( '(元の画像)' => array( 0, 0 ) );
foreach ( $ids as $id ) {
	$original = get_attached_file( $id );
	if ( ! $original || ! is_file( $original ) ) {
		continue;
	}
	$stat['(元の画像)'][0]++;
	$stat['(元の画像)'][1] += filesize( $original );

	$meta = wp_get_attachment_metadata( $id );
	if ( empty( $meta['sizes'] ) || ! is_array( $meta['sizes'] ) ) {
		continue;
	}
	$dir = dirname( $original );
	foreach ( $meta['sizes'] as $name => $size ) {
		if ( ! isset( $stat[ $name ] ) ) {
			$stat[ $name ] = array( 0, 0 );
		}
		$path = $dir . '/' . $size['file'];
		if ( is_file( $path ) ) {
			$stat[ $name ][0]++;
			$stat[ $name ][1] += filesize( $path );
		}
	}
}

$registered = array_keys( wp_get_registered_image_subsizes() );
foreach ( $stat as $name => $s ) {
	$note = ( '(元の画像)' === $name || in_array( $name, $registered, true ) ) ? '' : '  ← 今は登録されていない大きさ';
	WP_CLI::log( sprintf( '%-16s %7d ファイル %9.1f MB%s', $name, $s[0], $s[1] / 1048576, $note ) );
}

攻略サイトでの結果です。

(元の画像)      1065 ファイル     609.9 MB
medium              1065 ファイル      99.6 MB
large               1065 ファイル     709.6 MB
thumbnail           1065 ファイル      37.4 MB
medium_large        1065 ファイル     476.4 MB
size1               1065 ファイル      37.4 MB
size2               1065 ファイル     222.4 MB
size3               1065 ファイル     408.1 MB
size-card           1065 ファイル      24.3 MB
1536x1536            112 ファイル     123.7 MB

large(710MB)や size3(408MB)は大きいですが、使っているなら残します。問題は、使っていない大きさがどれかです。

手順 2:どの大きさを使っているかを調べる

画像が使われる場所は、主に 3 つあります。それぞれ確かめます。

(1) 記事の本文に入れた画像

エディタで画像を入れると、img に class=”size-large” のような印が付きます。これを数えます。

find-size-usage.php
<?php
// 公開中の記事・固定ページの本文に、どの大きさの画像が何か所入っているかを数える(読むだけ)
// 使い方(WordPress のフォルダで): wp eval-file find-size-usage.php
if ( ! defined( 'WP_CLI' ) || ! WP_CLI ) {
	exit;
}

global $wpdb;
$contents = $wpdb->get_col( "SELECT post_content FROM {$wpdb->posts} WHERE post_status = 'publish' AND post_type IN ( 'post', 'page' )" );

foreach ( array_keys( wp_get_registered_image_subsizes() ) as $name ) {
	$n = 0;
	foreach ( $contents as $content ) {
		// 画像を挿入すると img に class="size-大きさの名前" が付く(size-medium と size-medium_large を分けて数える)
		$n += preg_match_all( '/\bsize-' . preg_quote( $name, '/' ) . '(?![\w-])/', $content );
	}
	WP_CLI::log( sprintf( '%-16s %6d か所', $name, $n ) );
}
thumbnail             0 か所
medium                0 か所
medium_large          0 か所
large               947 か所
1536x1536             0 か所
2048x2048             0 か所
size1                 0 か所
size2                 0 か所
size3                 0 か所
size-card             0 か所

本文の画像は、すべて large でした。

(2) テーマのテンプレート

アイキャッチ画像などは、テーマのファイルの中で大きさを指定して出しています。テーマのフォルダを検索します。

ターミナル
grep -rnoE "(the_post_thumbnail|get_the_post_thumbnail|wp_get_attachment_image(_src|_url)?)\([^)]*\)" wp-content/themes/テーマのフォルダ
the_post_thumbnail('size1')        … 10 か所
the_post_thumbnail('size2')        … 4 か所
the_post_thumbnail('size3')        … 1 か所
wp_get_attachment_image_src( $post_thumbnail_id, 'thumbnail')
wp_get_attachment_image_src( $post_thumbnail_id, 'size1')

テーマは size1・size2・size3・thumbnail を使っていました(size-card は、テーマのほかのファイルで使っていました)。

(3) 実際のページの HTML

ページビルダーやプラグインが別の場所に画像を持っていることもあるので、最後に、公開しているページの HTML に出てくる画像のファイル名を見ます。ファイル名の最後の「-768×432」のような部分で、大きさが分かります。

ターミナル
curl -s https://example.com/archives/123 | grep -oE "[-_0-9a-zA-Z]+-[0-9]+x[0-9]+\.(jpg|jpeg|png|webp)" | sort | uniq -c

トップ・記事・一覧など、作りの違うページをいくつか見ます。

調べた結果

  • 使っている:large(本文)・size1〜3・size-card・thumbnail(テーマ)
  • 管理画面で使うので残す:medium(メディアの一覧の表示に使う)
  • どこでも使っていない:medium_large(476MB)・1536×1536(124MB)。srcset の候補に入っているだけ

medium_large などは srcset の候補として HTML に出るので、スマホなどの画面の幅によっては、ブラウザがこちらを選ぶことがあります。消すと、ブラウザはその次に合う大きさ(この例では large の 1024px)を選ぶので、スマホでの画像が少し重くなります。容量と表示の速さのどちらを取るかで決めてください。

手順 3:安全に消す

FTP やファイルマネージャーで「-768x」のファイルを消すだけだと、困ったことが起きます。

  • WordPress は、画像の情報(メタデータ)に書かれた大きさから srcset を作るので、消したファイルが srcset に残り、画像が表示されないことがある(404 になる)。
  • 同じファイル名を、残したい大きさも使っていることがある(元の画像が小さく、2 つの大きさが同じ寸法になったときなど)。
  • WebP に変えるプラグイン(Converter for Media など)は、別のフォルダに WebP のファイルを持っている。こちらは残ったままになる。

そこで、ファイルを消すと同時に、画像の情報からも外す台本を作りました。既定は数えるだけで、FS_DO=1 を付けたときだけ消します。

remove-image-sizes.php
<?php
// 使わない大きさの画像ファイルを消して、画像の情報(メタデータ)からも外す
// 元の画像と、残す大きさのファイルには触らない。Converter for Media の WebP のファイルも一緒に消す。
//
// 使い方(WordPress のフォルダで):
//   数えるだけ: wp eval-file remove-image-sizes.php
//   本当に消す: FS_DO=1 wp eval-file remove-image-sizes.php
if ( ! defined( 'WP_CLI' ) || ! WP_CLI ) {
	exit; // ブラウザから開かれても何もしない
}

$targets = array( 'medium_large', '1536x1536', '2048x2048' ); // 消す大きさ(サイトに合わせて変える)
$do      = ( '1' === getenv( 'FS_DO' ) );
$upload  = wp_get_upload_dir();
$webpc   = WP_CONTENT_DIR . '/uploads-webpc/uploads'; // Converter for Media の WebP の置き場所

$ids = get_posts(
	array(
		'post_type'      => 'attachment',
		'post_mime_type' => 'image',
		'post_status'    => 'inherit',
		'posts_per_page' => -1,
		'fields'         => 'ids',
	)
);

$count = array(
	'images'     => 0,
	'files'      => 0,
	'bytes'      => 0,
	'webp_files' => 0,
	'webp_bytes' => 0,
	'missing'    => 0,
	'shared'     => 0,
);

foreach ( $ids as $id ) {
	$meta = wp_get_attachment_metadata( $id );
	if ( empty( $meta['sizes'] ) || ! is_array( $meta['sizes'] ) ) {
		continue;
	}
	$original = get_attached_file( $id );
	if ( ! $original ) {
		continue;
	}
	$dir = dirname( $original );

	// 残すファイルの名前(元の画像と、残す大きさが使っているファイル)
	$keep = array( basename( $original ) => true );
	if ( ! empty( $meta['original_image'] ) ) {
		$keep[ $meta['original_image'] ] = true;
	}
	foreach ( $meta['sizes'] as $name => $size ) {
		if ( ! in_array( $name, $targets, true ) && ! empty( $size['file'] ) ) {
			$keep[ $size['file'] ] = true;
		}
	}

	$hit = false;
	foreach ( $targets as $name ) {
		if ( empty( $meta['sizes'][ $name ]['file'] ) ) {
			continue;
		}
		$hit  = true;
		$file = $meta['sizes'][ $name ]['file'];
		unset( $meta['sizes'][ $name ] ); // 画像の情報から外す(srcset に出なくなる)
		if ( isset( $keep[ $file ] ) ) {
			$count['shared']++; // 同じファイルを残す大きさが使っているので消さない
			continue;
		}
		$path = $dir . '/' . $file;
		if ( is_file( $path ) ) {
			$count['files']++;
			$count['bytes'] += filesize( $path );
			if ( $do ) {
				unlink( $path );
			}
		} else {
			$count['missing']++;
		}
		// WebP のファイル(uploads-webpc/uploads/年/月/ファイル名.webp)
		$relative = ltrim( str_replace( wp_normalize_path( $upload['basedir'] ), '', wp_normalize_path( $path ) ), '/' );
		$webp     = $webpc . '/' . $relative . '.webp';
		if ( is_file( $webp ) ) {
			$count['webp_files']++;
			$count['webp_bytes'] += filesize( $webp );
			if ( $do ) {
				unlink( $webp );
			}
		}
	}

	if ( $hit ) {
		$count['images']++;
		if ( $do ) {
			wp_update_attachment_metadata( $id, $meta );
		}
	}
}

WP_CLI::log(
	( $do ? '消した' : '数えただけ(消していない)' ) . sprintf(
		':画像 %d 枚・ファイル %d(%.1f MB)・WebP のファイル %d(%.1f MB)・見つからなかった %d・残す大きさと同じファイルで残した %d',
		$count['images'],
		$count['files'],
		$count['bytes'] / 1048576,
		$count['webp_files'],
		$count['webp_bytes'] / 1048576,
		$count['missing'],
		$count['shared']
	)
);

動かし方

ターミナル
cd ~/example.com/public_html        # WordPress のフォルダ(サーバーに合わせて変える)
wp eval-file remove-image-sizes.php            # 数えるだけ
FS_DO=1 wp eval-file remove-image-sizes.php    # 数を確かめてから、本当に消す

数えるだけの結果です。手順 1 の medium_large(1,065 ファイル)と 1536×1536(112 ファイル)の合計と合っていることを確かめてから、消しました(1 枚の違いは、その日にアップロードした画像です)。

数えただけ(消していない):画像 1066 枚・ファイル 1178(600.2 MB)・WebP のファイル 1176(86.3 MB)・見つからなかった 0・残す大きさと同じファイルで残した 0
  • サーバーの WP-CLI が新しい PHP に合っていないと、「PHP Deprecated: …」がたくさん出ることがあります。台本の動きとは関係ありません。見づらいときは wp eval-file remove-image-sizes.php 2>&1 | grep -v Deprecated で隠せます。
  • 台本はブラウザから開かれても何もしませんが、終わったらサーバーから消しておくと安心です。

消す前の控え

縮小版は元の画像から作り直せるもの(Regenerate Thumbnails などのプラグインで作り直せる)なので、ファイルを全部控える必要はあまりありません。それでも、レンタルサーバーの自動バックアップがあるか、消す前に確かめておきます。

消したあとに確かめること

  • トップ・記事・一覧などのページで、画像がすべて出るか。攻略サイトでは 8 ページに出てくる画像の URL 565 個を全部開いて、565 個とも表示されることを確かめました。
  • ページの HTML に、消した大きさ(-768x・-1536x)のファイル名が残っていないか(手順 2 の curl で確かめられます)。
  • PHP のエラーが出ていないか。

手順 4:これから作らせない

消しても、新しく画像を上げると、また同じ大きさが作られます。intermediate_image_sizes_advanced フィルターで、作る大きさの一覧から外します。

functions.php
/*----------------------------------------------------
 使わない大きさの画像を、これから作らない
----------------------------------------------------*/
if ( ! function_exists( 'fs_skip_image_sizes' ) ) {
	function fs_skip_image_sizes( $sizes ) {
		// medium_large(768px)・1536x1536・2048x2048 を作らない(サイトに合わせて変える)
		unset( $sizes['medium_large'], $sizes['1536x1536'], $sizes['2048x2048'] );
		return $sizes;
	}
	add_filter( 'intermediate_image_sizes_advanced', 'fs_skip_image_sizes' );
}
  • medium_large は設定の画面にないので、このフィルターで外すのが確実です。
  • テーマが使っている大きさ(手順 2 で見つけたもの)は外さないでください。外すと、その大きさの代わりに元の画像などが使われ、表示が崩れたり重くなったりします。
  • テーマを更新すると functions.php が上書きされるテーマでは、子テーマか、Code Snippets などに入れます。

WordPress 5.3 からは、幅や高さが 2560px を超える画像を自動で縮小し「-scaled」の付いたファイルを作ります(big_image_size_threshold)。これを止めると、数 MB の元の画像がそのまま使われることがあるので、止めないほうが安全です。

結果

  • 消したファイル:縮小版 1,178 + WebP のファイル 1,176 = 約 2,350 ファイル
  • 空いた容量:約 690MB(縮小版 600MB + WebP 86MB)
  • 表示:画像はすべて出る。HTML に消した大きさのファイル名は 0
  • これから上げる画像は、medium_large・1536×1536・2048×2048 を作らない

同じことを画像 約 2,000 枚の別のサイトでも数えましたが、使っていない大きさは約 43MB と小さかったので、そちらは見送りました。サイトによって差が大きいので、まず手順 1 で数えてから決めるのがおすすめです。

まとめ

  • まず大きさごとに数える(手順 1)。消すかどうかは、容量を見てから決める。
  • 使っているかは、本文・テーマ・実際のページの HTML の 3 つで確かめる。medium は管理画面で使うので残す。
  • ファイルだけ消さない。画像の情報(メタデータ)からも外し、WebP のファイルも一緒に消す。まず数えるだけで動かす。
  • intermediate_image_sizes_advanced で、これから作らせない。

参考

関連機能

  1. WordPress ショートリンク shortlink 短縮URLをソース内から削除する

  2. Simply Static で WordPress を静的 HTML にし、.htaccess で HTML があれば返す方法(ログイン中・検索・送信は WordPress へ)

  3. WordPressで特定のCSS や JS を読み込ませないようにする

サイドバー

よく使うカスタマイズ

最近の記事

Profile

PAGE TOP