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 と同じ形式のリクエストを渡して動作を確かめています。
構成
- WordPress で投稿が公開・更新されると、WP-Cron に「Lambda に処理を依頼する」予定を登録する(保存操作は待たされない)。
- WP-Cron が Lambda の関数 URL に POST する。改ざんやなりすましを防ぐため、共有の秘密鍵で HMAC-SHA256 の署名を付ける。
- Lambda は署名を確かめてから処理し、結果を JSON で返す。
- 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 に任せる構成です。