2026年8月21日

2026年8月21日

WordPressの重い処理をAWS Lambdaに任せる方法(関数URL・署名つき呼び出し・WP-Cron)

画像の加工、外部サービスからの大量取得、PDF の生成など、時間のかかる処理を WordPress(PHP)のリクエストの中で実行すると、画面の表示や保存が遅くなります。こうした処理を AWS Lambda に任せ、結果だけを WordPress に保存する構成を、手順とコードで説明します。

この記事の AWS の仕様は2026年11月時点の AWS 公式ドキュメントで確認したものです。WordPress 側のコードは WordPress 7.1.3(PHP 8.3)で、Lambda 側のコードは関数 URL と同じ形式のリクエストを渡して動作を確かめています。

構成

  1. WordPress で投稿が公開・更新されると、WP-Cron に「Lambda に処理を依頼する」予定を登録する(保存操作は待たされない)。
  2. WP-Cron が Lambda の関数 URL に POST する。改ざんやなりすましを防ぐため、共有の秘密鍵で HMAC-SHA256 の署名を付ける。
  3. Lambda は署名を確かめてから処理し、結果を JSON で返す。
  4. WordPress は結果をカスタムフィールドに保存する。

この記事では、処理の中身を「本文の文字数を数える」だけにしています。実際には、この部分を自分の重い処理に置き換えます。

Lambda を HTTP で呼ぶ方法には、関数 URL と Amazon API Gateway があります。WordPress からだけ呼ぶ単純な用途なら関数 URL で足ります。独自ドメイン、利用量の制限、複数の API をまとめた管理が必要なら API Gateway を検討します。

準備

  • AWS CLI v2 をインストールし、aws configure でリージョン(例: ap-northeast-1)と認証情報を設定しておきます。
  • 共有の秘密鍵を作っておきます。次のコマンドで64文字のランダムな文字列ができます。
openssl rand -hex 32

手順1: Lambda 用の IAM ロールを作る

Lambda 関数が使う実行ロールを作り、CloudWatch Logs にログを書く権限(AWS 管理ポリシー AWSLambdaBasicExecutionRole)を付けます。

trust-policy.json:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": { "Service": "lambda.amazonaws.com" },
      "Action": "sts:AssumeRole"
    }
  ]
}
aws iam create-role \
  --role-name wp-heavy-task-role \
  --assume-role-policy-document file://trust-policy.json

aws iam attach-role-policy \
  --role-name wp-heavy-task-role \
  --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole

create-role の結果に表示される Arn(arn:aws:iam::<アカウントID>:role/wp-heavy-task-role)を控えます。

手順2: Lambda 関数を作る

lambda_function.py を作ります。関数 URL から渡されるリクエスト(ペイロード形式 2.0)のヘッダーと本文を取り出し、署名と時刻を確かめてから処理します。

import base64
import hashlib
import hmac
import json
import os
import time

SECRET = os.environ["SHARED_SECRET"].encode("utf-8")


def response(status, data):
    return {
        "statusCode": status,
        "headers": {"Content-Type": "application/json"},
        "body": json.dumps(data, ensure_ascii=False),
    }


def lambda_handler(event, context):
    if event.get("requestContext", {}).get("http", {}).get("method") != "POST":
        return response(405, {"error": "POST only"})

    headers = {k.lower(): v for k, v in (event.get("headers") or {}).items()}
    body = event.get("body") or ""
    if event.get("isBase64Encoded"):
        body = base64.b64decode(body).decode("utf-8")

    # 署名の確認(時刻が5分以上ずれた要求と、署名が合わない要求は断る)
    ts = headers.get("x-mysite-timestamp", "")
    sig = headers.get("x-mysite-signature", "")
    if not ts.isdigit() or abs(time.time() - int(ts)) > 300:
        return response(401, {"error": "bad timestamp"})
    expected = hmac.new(SECRET, (ts + "." + body).encode("utf-8"), hashlib.sha256).hexdigest()
    if not hmac.compare_digest(expected, sig):
        return response(401, {"error": "bad signature"})

    data = json.loads(body)
    text = data.get("content", "")

    # ここに時間のかかる処理を書く(例として文字数を数えるだけにしている)
    result = {"post_id": data.get("post_id"), "chars": len(text)}

    return response(200, result)

ZIP にまとめて関数を作ります。<アカウントID> と秘密鍵は自分のものに置き換えます。ランタイムは、2026年11月時点でサポートされている python3.14 を指定しています(Python 3.8 などはすでにサポートが終了しています)。

zip function.zip lambda_function.py

aws lambda create-function \
  --function-name wp-heavy-task \
  --runtime python3.14 \
  --handler lambda_function.lambda_handler \
  --role arn:aws:iam::<アカウントID>:role/wp-heavy-task-role \
  --zip-file fileb://function.zip \
  --timeout 60 \
  --environment "Variables={SHARED_SECRET=ここに秘密鍵}"

ロールを作った直後は、ロールがまだ使えない旨のエラーになることがあります。その場合は数秒待ってからもう一度実行します。

--timeout は関数の最大実行時間(秒)で、初期値は3秒、上限は900秒(15分)です。処理にかかる時間に合わせて設定します。15分を超える処理は Lambda 1回では実行できないので、処理を分割するか、Amazon SQS や AWS Step Functions で複数回に分ける構成を検討します。

手順3: 関数 URL を作り、呼び出しを許可する

aws lambda create-function-url-config \
  --function-name wp-heavy-task \
  --auth-type NONE

結果の FunctionUrl(https://<url-id>.lambda-url.<リージョン>.on.aws/ の形式)が呼び出し先になります。

認証タイプ NONE の関数 URL でも、関数のリソースベースポリシーで公開アクセスを許可しないと 403 になります。AWS のドキュメントによると、2025年10月以降に作る関数 URL には lambda:InvokeFunctionUrl と lambda:InvokeFunction の両方の許可が必要です。コンソールで作った場合は自動で付きますが、CLI では2つのコマンドで追加します。

aws lambda add-permission \
  --function-name wp-heavy-task \
  --statement-id UrlPolicyInvokeURL \
  --action lambda:InvokeFunctionUrl \
  --principal "*" \
  --function-url-auth-type NONE

aws lambda add-permission \
  --function-name wp-heavy-task \
  --statement-id UrlPolicyInvokeFunction \
  --action lambda:InvokeFunction \
  --principal "*" \
  --invoked-via-function-url

--principal "*" は引用符で囲みます(囲まないとシェルがファイル名に展開することがあります)。--invoked-via-function-url を付けると、関数 URL 以外の経路からの呼び出しには許可が効きません。

認証タイプ NONE では、URL を知っている人は誰でも関数を呼べます。そのため、手順2のコードで署名を確かめ、合わない要求は処理せずに 401 を返しています。IAM 認証(AWS_IAM)にすることもできますが、その場合は WordPress 側で SigV4 署名を作る必要があります。

手順4: curl で呼び出しを確かめる

署名付きの要求と、署名なしの要求を送って結果を比べます。

FUNCTION_URL="https://<url-id>.lambda-url.ap-northeast-1.on.aws/"
SECRET="ここに秘密鍵"

body='{"post_id":1,"content":"テスト"}'
ts=$(date +%s)
sig=$(printf '%s' "${ts}.${body}" | openssl dgst -sha256 -hmac "$SECRET" | awk '{print $NF}')

# 署名あり → {"post_id": 1, "chars": 3}
curl -s -X POST "$FUNCTION_URL" \
  -H "Content-Type: application/json" \
  -H "X-Mysite-Timestamp: ${ts}" \
  -H "X-Mysite-Signature: ${sig}" \
  --data "$body"

# 署名なし → {"error": "bad timestamp"}(HTTP 401)
curl -s -X POST "$FUNCTION_URL" -H "Content-Type: application/json" --data "$body"

手順5: WordPress から呼び出す

まず wp-config.php に関数 URL と秘密鍵を書きます(require_once ABSPATH . 'wp-settings.php'; の行より上)。秘密鍵をテーマやプラグインのファイルに直接書かないためです。

<?php
define( 'MYSITE_LAMBDA_URL', 'https://<url-id>.lambda-url.ap-northeast-1.on.aws/' );
define( 'MYSITE_LAMBDA_SECRET', 'ここに秘密鍵' );

次のコードを wp-content/mu-plugins/mysite-lambda.php として保存します(mu-plugins フォルダーが無ければ作ります)。

<?php
/**
 * Plugin Name: Lambda 連携サンプル
 * Description: 投稿の公開・更新時に、Lambda 関数 URL へ処理を依頼して結果をカスタムフィールドに保存する。
 */

// 1. 投稿を公開・更新したら、Lambda への依頼を WP-Cron に積む(画面の保存を待たせない)
add_action( 'save_post_post', function ( $post_id, $post ) {
	if ( wp_is_post_revision( $post_id ) || wp_is_post_autosave( $post_id ) || 'publish' !== $post->post_status ) {
		return;
	}
	if ( ! wp_next_scheduled( 'mysite_lambda_task', array( $post_id ) ) ) {
		wp_schedule_single_event( time(), 'mysite_lambda_task', array( $post_id ) );
	}
}, 10, 2 );

// 2. WP-Cron の中で Lambda を呼び、結果を保存する
add_action( 'mysite_lambda_task', function ( $post_id ) {
	if ( ! defined( 'MYSITE_LAMBDA_URL' ) || ! defined( 'MYSITE_LAMBDA_SECRET' ) ) {
		return;
	}
	$post = get_post( $post_id );
	if ( ! $post ) {
		return;
	}

	$body = wp_json_encode(
		array(
			'post_id' => $post_id,
			'content' => wp_strip_all_tags( $post->post_content ),
		)
	);
	$timestamp = (string) time();
	$signature = hash_hmac( 'sha256', $timestamp . '.' . $body, MYSITE_LAMBDA_SECRET );

	$response = wp_remote_post(
		MYSITE_LAMBDA_URL,
		array(
			'timeout' => 60,
			'headers' => array(
				'Content-Type'       => 'application/json',
				'X-Mysite-Timestamp' => $timestamp,
				'X-Mysite-Signature' => $signature,
			),
			'body'    => $body,
		)
	);

	if ( is_wp_error( $response ) ) {
		error_log( 'Lambda error: ' . $response->get_error_message() );
		return;
	}
	$code = wp_remote_retrieve_response_code( $response );
	if ( 200 !== $code ) {
		error_log( 'Lambda HTTP ' . $code . ': ' . wp_remote_retrieve_body( $response ) );
		return;
	}

	$data = json_decode( wp_remote_retrieve_body( $response ), true );
	if ( isset( $data['chars'] ) ) {
		update_post_meta( $post_id, '_mysite_char_count', (int) $data['chars'] );
	}
} );

ポイントは次のとおりです。

  • save_post_post は「投稿」タイプの保存時だけ実行されるフックです。リビジョンと自動保存、公開以外の状態は除外しています。
  • Lambda の呼び出しは wp_schedule_single_event() で WP-Cron に回しているので、編集画面の保存は Lambda の処理を待ちません。
  • wp_remote_post() の timeout は、Lambda の --timeout 以上にします。短いと、Lambda が処理中でも WordPress 側があきらめてしまいます。
  • 署名は「時刻 + . + 送信する本文そのもの」に対して計算します。Lambda 側も受け取った本文そのもので計算するので、JSON の書式の違いで不一致になることはありません。

WP-CLI で動作を確かめます。

# 投稿を公開すると予定が登録される
wp cron event list --fields=hook,args,next_run_relative

# 期限が来た予定をすぐに実行する
wp cron event run --due-now

# 結果を確認(投稿 ID 13 の例)
wp post meta get 13 _mysite_char_count

検証環境では、本文が「こんにちは世界」の投稿を公開したあと wp cron event run --due-now を実行し、_mysite_char_count に 7 が保存されることを確かめました。

手順6: ログを確認する

Lambda の print() やエラーは CloudWatch Logs のロググループ /aws/lambda/<関数名> に記録されます。AWS CLI v2 では次のコマンドで追いかけて表示できます。

aws logs tail /aws/lambda/wp-heavy-task --follow

WordPress 側の失敗(タイムアウトや 401 など)は、error_log() で PHP のエラーログに記録されます。

運用の注意

  • WP-Cron はアクセスがあったときに動きます。 アクセスが少ないサイトでは実行が遅れるので、サーバーの cron から wp cron event run --due-now を定期的に実行する方法を検討します。
  • 失敗時の再実行はありません。 このサンプルは失敗をログに残すだけです。失敗した処理をやり直したい場合は、失敗したら wp_schedule_single_event() で少し後に再登録するなどの仕組みを加えます。同じ投稿を2回処理しても結果が変わらない処理にしておくと安全です。
  • 秘密鍵の扱い。 Lambda の環境変数は、Lambda の設定を見られる人には読めます。より厳しく管理したい場合は AWS Secrets Manager の利用を検討します。秘密鍵を変えるときは、Lambda と wp-config.php の両方を更新します。
  • 料金。 Lambda はリクエスト数と実行時間(割り当てたメモリ量に応じて)で課金されます。最新の料金は AWS の料金ページで確認してください。
  • WordPress 自体を Lambda で動かす構成とは別物です。 この記事は、WordPress は通常のサーバーで動かし、重い処理だけを Lambda に任せる構成です。

関連記事

お気軽にご相談ください

お見積りへ お問い合わせへ
▲