2026年5月20日
2026年5月20日
WordPressのカスタムフィールドが保存されない原因と直し方|save_post・nonce・ブロックエディター
投稿にカスタムフィールドの値を入れて保存したのに消えてしまう、自作のメタボックスの値が反映されない、update_post_meta() が動いていないように見える、という問題の原因と直し方を説明します。内容は2026年10月時点のWordPress(ブロックエディター)を前提にしています。
コードは子テーマの functions.php か、独自のプラグインに書きます。変更前にバックアップを取り、できればステージング環境で試してください。
よくある原因
| 原因 | 起きること |
|---|---|
| nonce のアクション名・フィールド名が、出力側と確認側で一致していない | 検証で処理が止まり、何も保存されない |
| 入力欄が送られていないリクエストでも保存処理が動く | 空の値で上書きされ、保存したのに消える |
| 権限の確認を間違えている | 管理者以外のユーザーだけ保存されない |
カスタム投稿タイプの supports に custom-fields がない |
カスタムフィールドのパネルが出ない、REST API でメタを扱えない |
register_post_meta() の条件が足りない |
ブロックエディター(REST API)からの保存が無視される |
ブロックエディターでは保存処理が2回呼ばれる
ブロックエディターで「更新」を押すと、次の2つのリクエストが順に送られます。
- 本文やタイトルを REST API で保存するリクエスト
- 画面下部のメタボックスの入力内容を
post.phpに送るリクエスト(メタボックスがある場合のみ)
save_post アクションはどちらのリクエストでも実行されます。1つ目のリクエストにはメタボックスの入力欄の値が含まれないため、次のように書くと、そのたびに空の値で上書きされます。
<?php
// よくない例:入力欄がないリクエストでも空文字で上書きしてしまう
add_action( 'save_post', function ( $post_id ) {
update_post_meta( $post_id, 'my_custom_field', $_POST['my_custom_field'] ?? '' );
} );
保存処理は「入力欄とnonceが送られてきたときだけ」動くようにします。次の手順のコードはその形になっています。
ステップ1:メタボックスとnonceを出力する
<?php
// 投稿の編集画面にメタボックスを追加する
add_action( 'add_meta_boxes', function () {
add_meta_box(
'my_meta_box', // メタボックスのID
'カスタム情報', // 見出し
'my_render_meta_box', // 中身を出力する関数
'post', // 対象の投稿タイプ
'normal',
'high'
);
} );
function my_render_meta_box( $post ) {
// nonce を出力する(アクション名とフィールド名を保存側と合わせる)
wp_nonce_field( 'my_save_meta', 'my_meta_nonce' );
$value = get_post_meta( $post->ID, '_my_custom_field', true );
echo '<label for="my_custom_field">カスタム値:</label>';
echo '<input type="text" id="my_custom_field" name="my_custom_field" value="'
. esc_attr( $value ) . '" style="width:100%">';
}
メタキーを _my_custom_field のようにアンダースコアで始めると、「保護されたメタ」として扱われ、標準のカスタムフィールドのパネルには表示されません。自作のメタボックスだけで編集したい値は、この形にすると、同じ値を2か所で編集してしまうことを防げます。
ステップ2:save_post で保存する
<?php
// メタボックスの値を保存する
add_action( 'save_post', function ( $post_id ) {
// 自動保存では何もしない
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
return;
}
// リビジョンには保存しない
if ( wp_is_post_revision( $post_id ) ) {
return;
}
// nonce がない(=メタボックスの送信ではない)、または正しくない場合は何もしない
if ( ! isset( $_POST['my_meta_nonce'] )
|| ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['my_meta_nonce'] ) ), 'my_save_meta' ) ) {
return;
}
// 編集権限の確認
if ( ! current_user_can( 'edit_post', $post_id ) ) {
return;
}
// 入力欄が送られてきたときだけ保存する
if ( isset( $_POST['my_custom_field'] ) ) {
update_post_meta(
$post_id,
'_my_custom_field',
sanitize_text_field( wp_unslash( $_POST['my_custom_field'] ) )
);
}
} );
確認するポイントは次のとおりです。
wp_nonce_field()の1つ目の引数(アクション名)とwp_verify_nonce()の2つ目の引数が同じかwp_nonce_field()の2つ目の引数(フィールド名)と$_POSTのキーが同じか- 権限は
current_user_can( 'edit_post', $post_id )のように投稿IDを付けて確認しているか(edit_postは投稿ごとの権限なので、IDを渡さないと正しく判定されません) $_POSTの値はwp_unslash()でスラッシュを取り除いてから無害化しているか
特定の投稿タイプだけを対象にする場合は、save_post_post(save_post_{投稿タイプ})フックを使うと、投稿タイプの判定を書かずに済みます。
ステップ3:ブロックエディターでカスタムフィールドのパネルを表示する
標準のカスタムフィールドのパネル(メタキーと値を直接入力する欄)は、ブロックエディターでは初期状態で非表示です。ブロックエディターには「表示オプション」はないため、次の手順で表示します。
- 編集画面の右上にある「⋮」(オプション)をクリックする
- 「設定」を開く
- 「一般」タブの「高度な設定」にある「カスタムフィールド」をオンにする
- 「ページを再読み込みして表示」をクリックする(再読み込みの前に、編集中の内容を保存しておく)
「カスタムフィールド」の項目が出ない場合は、その投稿タイプが custom-fields をサポートしていません(次のステップ)。また、アンダースコアで始まるメタキーは、このパネルには表示されません。
ステップ4:カスタム投稿タイプに custom-fields を追加する
<?php
// カスタム投稿タイプを登録する(supports に custom-fields を含める)
add_action( 'init', function () {
register_post_type( 'my_type', array(
'label' => 'マイタイプ',
'public' => true,
'show_in_rest' => true, // ブロックエディターで編集するために必要
'supports' => array( 'title', 'editor', 'thumbnail', 'custom-fields' ),
) );
} );
すでに登録済みの投稿タイプに追加する場合は、add_post_type_support( 'my_type', 'custom-fields' ); を init で実行します。
ステップ5:ブロックエディターから保存するメタを登録する
ブロックエディターのサイドバーやブロックから(JavaScript で)メタを編集する場合、値は REST API で保存されます。このとき、メタを register_post_meta() で show_in_rest 付きで登録していないと、値は保存されません。
<?php
// REST API(ブロックエディター)から読み書きできるメタとして登録する
add_action( 'init', function () {
register_post_meta( 'post', '_my_custom_field', array(
'show_in_rest' => true,
'single' => true,
'type' => 'string',
'sanitize_callback' => 'sanitize_text_field',
// アンダースコアで始まる(保護された)メタキーは、auth_callback がないと REST API から更新できない
'auth_callback' => function ( $allowed, $meta_key, $post_id ) {
return current_user_can( 'edit_post', $post_id );
},
) );
} );
REST API でメタを扱えるのは、その投稿タイプが custom-fields をサポートしている場合だけです。カスタム投稿タイプでは、ステップ4の設定も確認してください。
ステップ1〜2のメタボックスだけで編集する場合、この登録は必須ではありません。メタボックスの値は REST API ではなく、post.php へのリクエストで保存されるためです。
ステップ6:ACFなどのプラグインを使う
項目が多い場合や、画像・繰り返しの入力欄が必要な場合は、Advanced Custom Fields(ACF)のようなプラグインを使うと、保存処理を自分で書かずに済みます。
# WP-CLI で ACF(無料版)をインストールして有効化する
wp plugin install advanced-custom-fields --activate
管理画面の「ACF」→「フィールドグループ」から新しいフィールドグループを作り、フィールドを追加して、そのフィールドグループを表示する条件(投稿タイプなど)を指定します。繰り返しの入力欄(リピーターフィールド)は有料版の ACF PRO の機能です。
ACF を使っている場合に保存されないときは、フィールドグループの表示条件が編集中の投稿に合っているか、同じ名前のフィールドを自作のコードでも保存していないかを確認します。
ステップ7:保存処理が動いているか確認する
wp-config.php で WP_DEBUG と WP_DEBUG_LOG を true にすると、error_log() の出力が wp-content/debug.log に記録されます。
<?php
// 一時的な確認用:保存処理がどのリクエストで動いたかを記録する
add_action( 'save_post', function ( $post_id ) {
error_log( sprintf(
'save_post: id=%d rest=%s nonce=%s field=%s',
$post_id,
( defined( 'REST_REQUEST' ) && REST_REQUEST ) ? 'yes' : 'no',
isset( $_POST['my_meta_nonce'] ) ? 'yes' : 'no',
isset( $_POST['my_custom_field'] ) ? 'yes' : 'no'
) );
}, 5 );
ブロックエディターで更新すると、rest=yes(REST API の保存)と rest=no nonce=yes(メタボックスの送信)の2行が記録されるはずです。2行目が記録されない場合は、メタボックスが表示されていないか、別の投稿タイプに追加しています。nonce=yes なのに保存されない場合は、ステップ2の nonce のアクション名と権限の確認を見直します。
確認が終わったら、このコードは削除し、WP_DEBUG も元に戻してください。
まとめ
- ブロックエディターでは
save_postが2回実行される。入力欄と nonce が送られてきたときだけ保存し、空の値で上書きしない - nonce のアクション名・フィールド名を出力側と保存側で合わせ、権限は
current_user_can( 'edit_post', $post_id )で確認する - カスタムフィールドのパネルは「⋮」→「設定」→「一般」→「高度な設定」→「カスタムフィールド」で表示する
- カスタム投稿タイプは
supportsにcustom-fieldsを含める - ブロックエディターの JavaScript から保存するメタは
register_post_meta()にshow_in_restを付けて登録する
関連記事: