WordPress の遅いページを突き止める

Webサイトの速さを図るにはさまざまな指標・ツールがあります。有名なものとしてはGoogleが検索順位の指標にしているCore Web Vitalとそれを測ってくれるLighthouseがあります。このツールは主にフロントエンドの改善に役立ちますね。

さて、サイトの速さを図る指標の1つにTTFB(Time To First Byte)という概念があります。いまはあまりこの言葉自体は使われなくなったようですが、ブラウザがHTMLを受け取り始めるまでの時間(ミリ秒)を意味します。このTTFBは200ミリ秒が推奨されており、300-500ミリ秒だと普通、それを超えると「遅い」となります。

Chrome開発者ツールでサーバー応答時間を確認できます。

このTTFBに影響するのはSSLネゴシエーションなどの通信関係の時間もありますが、サーバーで色々動作している時間も含まれるので、ここが遅いと一気に2秒とかになってしまいます。WordPressの場合でいえばPHPおよびMySQLの処理時間が多くを占めるでしょう。

2秒とかはまずいので、これをせめて600ミリ秒以下にしたい、となったとします。が、問題はこれにどうやって気づくか、という点です。「自分で見ていて遅いと気づいた」というのはラッキーなレアケース、「お客さんが気づいて報告してきた」というのは怒られるかもしれませんが気づいたのでまだ良い方です。よくないのは遅いまま放置されててユーザーは離れていっているのだけど運営側は気づいていないというケースですね。

タロスカイで現状やっていること

タロスカイではこのTTFB(サーバー応答速度)を図るためにPHP-FPMのスローログとMySQLのスロークエリを活用しています。これらは全てのEC2インスタンスからCloudWatchに転送され、閾値を超えると通知が届きます。

これはこれで改善に役立つのですが、それぞれ以下のようなハマりポイントがあります。

  • MySQLのスロークエリは「それが何によって引き起こされているのかわからない」ケースがある。サードパーティープラグインが原因だった場合は時間がかかることも。
  • PHP-FPMのスローログは根本的な原因を表示しないことが多い。WordPressのプロセス全体が遅いということだけがわかる。

両者に共通するのは、そのログが出たURLがどこなのかすぐわからないという点です。ここがわかると楽なのですけどね。現状は職人技によって突き止めています。

WordPressの処理時間をページ単位で突き止める

さて、そこで「実際問題、WordPressはどれぐらい遅いの?」という謎を網羅的に調べるために、次のような簡単なプラグインを作ってみました。WordPressの起動から終了までを計測し、擬似的なTTFBとしてはかり、GA4に投げます。このファイルをmu-pluginsとして保存します。

/**
 * 常に開始時点のマイクロ秒を返す
 */
function ts_slow_log_start_time() {
	static $start = null;
	if ( is_null( $start ) {
		$start = microtime( true );
	}
	return $start;
}

// すぐ実行して開始時間を記録
ts_slow_log_start_time();

/**
 * フッターの一番最後にGA4用のコードを出す
 * 
 * GAはどこかに読み込み済みという想定です。GTMなどの場合は少し変わるかも?
 */
add_action( 'wp_footer', function() {
	if ( is_admin() ) {
		return;
	}
	// かかった時間をマイクロ秒(少数)からミリ秒(整数)に変換
	$total     = ( microtime( true ) - ts_slow_log_start_time() ) * 1000;
	$server_ms = (int) round( $total ); // サーバー処理時間(概算)
	// 任意: ルート分類などを付けたい場合にここでなんらかの分類を返す。カスタム投稿タイプとか、アーカイブとか
	// この関数自体は未定義です。
	$route = ts_slowrequest_route_bucket();
	?>
	<script>
		(function() {
			window.dataLayer = window.dataLayer || [];
			function gtag() {
				dataLayer.push( arguments );
			}
			gtag( 'event', 'wp_server_time', {
				value: <?php echo (int) $server_ms; ?>,
				route: '<?php echo esc_js( $route ); ?>'
			} );
		})();
	</script>
	<?php 
}, PHP_INT_MAX );

GA4側でカスタム指標として wp_server_time を用意しておく必要があります。routeというのはページ種別で、作成しているサイトごとにいい感じで分類してみてください。しばらく経つとGA4にこんな感じでデータが溜まります。

GA4にたまった wp_server_time

レポートにする

GA4なのはあくまで例であり、CloudWatch+SNSトピック購読でもよいと思われます。ただ、私はGA4を選んでしまった(他の人も見られるかな、と思ったので……)ので、なんとBigQueryまで使うハメになりました。GA4の計算指標(?)がうまく使えなかったので、ヒットあたりの平均ミリ秒が出せなかった……。

SELECT
    page_path,
    COUNT(*) AS hit_count,
    ROUND(AVG(server_time_ms), 1) AS avg_server_time_ms,
    SUM(server_time_ms) AS total_server_time_ms
FROM (
    SELECT
        (SELECT value.string_value FROM UNNEST(event_params) WHERE key = "page_location") AS page_path,
        SAFE_CAST((SELECT value.int_value FROM UNNEST(event_params) WHERE key = "value") AS INT64) AS server_time_ms
    FROM
        `【プロジェクトID】.analytics_【プロパティID】.events_*`
    WHERE
        _TABLE_SUFFIX BETWEEN FORMAT_DATE('%Y%m%d', DATE_SUB(CURRENT_DATE(), INTERVAL 7 DAY))
        AND FORMAT_DATE('%Y%m%d', CURRENT_DATE())
        AND event_name = 'wp_server_time'
)
WHERE server_time_ms IS NOT NULL
GROUP BY page_path
ORDER BY avg_server_time_ms DESC
LIMIT 10

BigQueryでは上記のような感じでクエリを書いて、なんらかの方法で出力します。たぶんCloudStorageに吐き出して何かゴニョゴニョするとか、Looker Studioに一回インポートしてからPDFで配信スケジュールを使うとか、そのような方式になるかと思います。まだここやってないです。すいません。

というわけで、こんな感じで「WordPressサイトで遅い部分を見つける」という仕組みをいくつか仕込んでおくといつしか高速なWordPressに育っていってくれるはずです。ついついサイトの同じ部分ばかり見てしまいますが、網羅的に問題点を見つけ出すためにもこのツールを有効活用しましょう。ここらへんも生成AIが勝手にやってくんないかなーと思う昨今でした。