
WordPress REST API 完全ガイド - 認証・カスタムエンドポイント・ヘッドレスCMS活用まで
WordPress REST API とは
WordPress REST API は、WordPress 4.7(2016年)でコアに統合された JSON ベースの公式 API です。/wp-json/ を起点として、投稿・固定ページ・メディア・ユーザー・カテゴリーなどあらゆるリソースを HTTP 経由で読み書きできます。
これによって WordPress は単なる CMS ではなく、「ヘッドレスCMS」「モバイルアプリのバックエンド」「他システムとの連携 API」 としても活用できる存在になりました。
本記事は WordPress 6.8 系・2026年5月時点 の公式リファレンス(developer.wordpress.org/rest-api/)に基づきます。
REST API の基本情報
| 項目 | 内容 |
|---|---|
| ルート URL | https://your-site.com/wp-json/ |
| コア名前空間 | /wp/v2/ |
| データ形式 | JSON |
| HTTPメソッド | GET / POST / PUT / PATCH / DELETE |
| 認証 | Cookie (X-WP-Nonce) / Application Passwords / JWT (プラグイン) / OAuth |
| プラグイン | 標準で同梱(WP 4.7+) |
ブラウザで https://your-site.com/wp-json/ を開くと、利用可能な名前空間とエンドポイントの一覧が JSON で返ってきます。これが REST API の インデックス(discovery エンドポイント) で、どのリソースがあるかを動的に発見できます。
主要エンドポイント一覧
WordPress コアが提供する /wp/v2/ 名前空間の主なエンドポイントです。
| エンドポイント | 用途 |
|---|---|
/wp/v2/posts | 投稿の一覧/作成/更新/削除 |
/wp/v2/posts/<id> | 単一投稿 |
/wp/v2/pages | 固定ページ |
/wp/v2/media | メディア(画像・動画など) |
/wp/v2/users | ユーザー |
/wp/v2/users/me | 認証済みユーザーの情報 |
/wp/v2/categories | カテゴリー |
/wp/v2/tags | タグ |
/wp/v2/comments | コメント |
/wp/v2/taxonomies | タクソノミー定義 |
/wp/v2/types | 投稿タイプ定義 |
/wp/v2/search | 横断検索 |
/wp/v2/settings | サイト設定(要認証) |
/wp/v2/menus | ナビメニュー(WP 5.9+) |
/wp/v2/blocks | 再利用可能ブロック |
よくあるクエリパラメータ
# 公開済みの最新投稿を10件
GET /wp-json/wp/v2/posts?per_page=10&orderby=date&order=desc
# slug 指定で取得
GET /wp-json/wp/v2/posts?slug=hello-world
# 特定カテゴリーで絞り込み
GET /wp-json/wp/v2/posts?categories=5
# 必要なフィールドだけ返す
GET /wp-json/wp/v2/posts?_fields=id,title,slug,date
# 関連リソースを埋め込む(_embed)
GET /wp-json/wp/v2/posts?_embed特に _fields と _embed は オーバーフェッチ抑制 に必須です。
認証方法を整理する
REST API の認証は用途で使い分けます。
| 認証方法 | 使うシーン | 特徴 |
|---|---|---|
| Cookie + X-WP-Nonce | 同一サイト内(管理画面 JS など) | デフォルト・最もシンプル |
| Application Passwords | 外部スクリプト・CI・サードパーティ | WP 5.6+ 標準。HTTPS 必須 |
| JWT (プラグイン) | SPA / モバイル | プラグイン要、トークン制 |
| OAuth 2.0 (プラグイン) | サードパーティ統合 | 委任認可が必要なときだけ |
1. Cookie + X-WP-Nonce(同一サイトJSの定石)
WordPress にログインしているユーザーが、管理画面の JS から API を叩く場合の標準。サーバー側で wp_create_nonce('wp_rest') で発行された nonce をリクエストヘッダ X-WP-Nonce に入れます。
// wp_localize_script() で wpApiSettings.nonce が出力されている前提
fetch(`${wpApiSettings.root}wp/v2/posts/1`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-WP-Nonce': wpApiSettings.nonce,
},
body: JSON.stringify({ title: 'Hello Moon' }),
})
.then((r) => r.json())
.then(console.log);サーバー側でカスタム検証する場合:
$valid = wp_verify_nonce( $_SERVER['HTTP_X_WP_NONCE'] ?? '', 'wp_rest' );2. Application Passwords(推奨:外部からの API 利用)
WordPress 5.6 以降の標準機能 で、ユーザーごとに発行できる 24文字のトークン です(公式ドキュメント)。
発行手順
- WP 管理画面 → ユーザー → 自分のプロフィール → 「アプリケーションパスワード」
- アプリ名を入力 → 「新しいアプリケーションパスワードを追加」
- 表示された 24 文字のパスワードを控える(再表示不可)
curl での利用
curl -X GET https://your-site.com/wp-json/wp/v2/posts \
-u "your-username:ABCD 1234 EFGH 5678 IJKL 9012"スペース込みのパスワードをそのまま Basic 認証で送ります(WordPress 側でスペースは無視)。
Node.js から呼び出す例
import { Buffer } from "node:buffer";
const user = process.env.WP_USER;
const appPass = process.env.WP_APP_PASSWORD; // "ABCD 1234 ..."
const auth = Buffer.from(`${user}:${appPass}`).toString("base64");
const res = await fetch("https://your-site.com/wp-json/wp/v2/posts", {
method: "POST",
headers: {
"Authorization": `Basic ${auth}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
title: "API から投稿",
content: "<p>Hello from Node.js!</p>",
status: "publish",
}),
});
console.log(await res.json());重要な制約
- HTTPS が必須(localhost を除く)。HTTP では Application Passwords は機能しない
- wp-login.php からは利用不可(API 専用)
- ユーザー側でいつでも個別に revoke できる
- 利用可能か事前確認:
wp_is_application_passwords_available()
3. JWT 認証(SPA/モバイル向け)
短命なアクセストークンが必要な SPA/モバイルでは、JWT Authentication for WP REST API などのプラグインで JWT を発行して Authorization: Bearer <token> で送る方式が一般的です。リフレッシュ/ローテーションは自前管理が必要なので、要件次第で Application Passwords とどちらを取るか決めます。
カスタムエンドポイントを作る - register_rest_route
WordPress プラグインや functions.php で、自前のエンドポイントを定義できます。WP 5.5 以降、permission_callback の指定は実質必須 で、未指定だとログ警告が出ます。
最小サンプル:公開エンドポイント
add_action( 'rest_api_init', function () {
register_rest_route( 'mytheme/v1', '/hello', array(
'methods' => WP_REST_Server::READABLE, // 'GET'
'callback' => 'mytheme_hello',
'permission_callback' => '__return_true', // 公開ならこれ
) );
} );
function mytheme_hello( WP_REST_Request $request ) {
return rest_ensure_response( array(
'message' => 'Hello, REST!',
'time' => current_time( 'mysql' ),
) );
}呼び出し:
curl https://your-site.com/wp-json/mytheme/v1/hello
# {"message":"Hello, REST!","time":"2026-05-07 19:30:00"}認可付き:管理者のみアクセス可
add_action( 'rest_api_init', function () {
register_rest_route( 'mytheme/v1', '/private', array(
'methods' => WP_REST_Server::READABLE,
'callback' => 'mytheme_private_data',
'permission_callback' => function () {
if ( ! current_user_can( 'manage_options' ) ) {
return new WP_Error(
'rest_forbidden',
__( 'この情報を見る権限がありません', 'mytheme' ),
array( 'status' => 401 )
);
}
return true;
},
) );
} );
function mytheme_private_data() {
return rest_ensure_response( array( 'secret' => 'classified' ) );
}URL パラメータ + 検証 + サニタイズ
add_action( 'rest_api_init', function () {
register_rest_route( 'mytheme/v1', '/author/(?P<id>\d+)', array(
'methods' => 'GET',
'callback' => 'mytheme_get_author',
'args' => array(
'id' => array(
'description' => '投稿者の ID',
'type' => 'integer',
'required' => true,
'validate_callback' => fn( $v ) => is_numeric( $v ) && (int) $v > 0,
'sanitize_callback' => 'absint',
),
),
'permission_callback' => '__return_true',
) );
} );
function mytheme_get_author( WP_REST_Request $request ) {
$id = (int) $request['id'];
$user = get_userdata( $id );
if ( ! $user ) {
return new WP_Error( 'not_found', '投稿者が見つかりません', array( 'status' => 404 ) );
}
return rest_ensure_response( array(
'id' => $user->ID,
'display_name' => $user->display_name,
'posts_count' => count_user_posts( $user->ID, 'post' ),
) );
}POST で書き込み + JSON ボディ
add_action( 'rest_api_init', function () {
register_rest_route( 'mytheme/v1', '/feedback', array(
'methods' => WP_REST_Server::CREATABLE, // 'POST'
'callback' => 'mytheme_feedback',
'permission_callback' => function ( WP_REST_Request $request ) {
return is_user_logged_in();
},
'args' => array(
'rating' => array(
'type' => 'integer',
'required' => true,
'validate_callback' => fn( $v ) => is_numeric( $v ) && $v >= 1 && $v <= 5,
'sanitize_callback' => 'absint',
),
'comment' => array(
'type' => 'string',
'required' => false,
'sanitize_callback' => 'sanitize_textarea_field',
),
),
) );
} );
function mytheme_feedback( WP_REST_Request $request ) {
$user_id = get_current_user_id();
$rating = (int) $request['rating'];
$comment = (string) ( $request['comment'] ?? '' );
add_user_meta( $user_id, 'mytheme_feedback', compact( 'rating', 'comment' ) );
return rest_ensure_response( array( 'ok' => true ) );
}ポイント:
validate_callbackは値の妥当性を検証(不正なら 400)sanitize_callbackは値を整形(XSS/インジェクション対策)permission_callbackは実行可否を判定(不可なら 401/403)
この 3点セット がカスタムエンドポイント設計の基本です。
ヘッドレスCMSとして使う - Next.js との組み合わせ
REST API があれば、WordPress を 「コンテンツ管理だけ」 に使い、表示は Next.js などモダンフロントエンドで行う ヘッドレス構成 が組めます。
Next.js 16 での投稿一覧取得
type WPPost = {
id: number
slug: string
title: { rendered: string }
excerpt: { rendered: string }
date: string
}
async function getPosts(): Promise<WPPost[]> {
const res = await fetch(
"https://wp.example.com/wp-json/wp/v2/posts?per_page=10&_fields=id,slug,title,excerpt,date",
{ next: { revalidate: 600, tags: ["wp-posts"] } } // 10分キャッシュ + タグ再検証
)
if (!res.ok) throw new Error("fetch failed")
return res.json()
}
export default async function BlogIndex() {
const posts = await getPosts()
return (
<ul>
{posts.map((p) => (
<li key={p.id}>
<a href={`/blog/${p.slug}`} dangerouslySetInnerHTML={{ __html: p.title.rendered }} />
</li>
))}
</ul>
)
}REST API vs WPGraphQL(プラグイン)
ヘッドレス用途では WPGraphQL プラグインを入れて GraphQL で叩く選択肢もあります。
| 観点 | REST API | WPGraphQL |
|---|---|---|
| 導入 | 標準同梱 | プラグインが必要 |
| 学習コスト | 低(HTTP/JSON) | 中(GraphQLの理解) |
| データ取得 | エンドポイントごとに固定 | クエリで必要な分だけ |
| 関連取得 | _embed か複数リクエスト | 1リクエストで深く取れる |
| キャッシュ | HTTP/CDNと相性良い | カスタム工夫が必要 |
| エコシステム | 巨大 | Apollo/urql 等で快適 |
「シンプルなブログ/ニュースサイトは REST、商品×レビュー×在庫など関連が深いサイトは WPGraphQL」が大まかな選定基準です。
キャッシュ戦略
WordPress 本体側で重い処理を毎リクエストやるとしんどいので、Transients API や Object Cache(Redis) との併用がほぼ必須です。詳しくは WordPress Transients API 完全ガイド を参照してください。
Next.js 側でも revalidate / revalidateTag / revalidatePath を使って ISR キャッシュを併用するのが定石です。
落とし穴とセキュリティ対策
REST API を有効にしただけで、いくつかの 意図せぬ情報露出 が発生し得ます。
ワナ 1:ユーザー列挙(/wp/v2/users)
匿名でも /wp-json/wp/v2/users を叩くと、投稿者の slug(ログイン名と同じケースが多い) が一覧で返ります。これはブルートフォース攻撃の前段になります。
対策:未認証ユーザーには users を見せない
add_filter( 'rest_endpoints', function ( $endpoints ) {
if ( is_user_logged_in() ) {
return $endpoints;
}
foreach ( array( '/wp/v2/users', '/wp/v2/users/(?P<id>[\d]+)' ) as $route ) {
if ( isset( $endpoints[ $route ] ) ) {
unset( $endpoints[ $route ] );
}
}
return $endpoints;
} );セキュリティ系プラグイン(Wordfence、SiteGuard など)でも同等の遮断ができます。
ワナ 2:CSRF(クロスサイトリクエストフォージェリ)
ログイン中ユーザーのブラウザから攻撃サイト経由で API を叩かれる。
対策:同一サイトの JS で API を叩く場合は必ず X-WP-Nonce を送信する。サードパーティアプリには Application Passwords を使い、Cookie 認証は避ける。
ワナ 3:レート制限なし
WordPress コアにはレート制限がありません。総当たり攻撃や Bot による暴走に弱いです。
対策:
- リバースプロキシ(Apache/Nginx)で IP 単位の
mod_evasive/limit_req - CDN/WAF(Cloudflare の Rate Limiting Rules)
- プラグイン(Limit Login Attempts Reloaded など)
ワナ 4:REST API を完全無効化したい場合
「サイト内でも REST API は使わないから止めたい」要件はあり得ますが、ブロックエディタや一部コア機能が REST に依存している ため完全 OFF はおすすめしません。
代替として、特定エンドポイントのみ匿名拒否 が現実解です。
add_filter( 'rest_authentication_errors', function ( $result ) {
if ( ! empty( $result ) ) {
return $result; // 既にエラー判定済みならそれを尊重
}
if ( ! is_user_logged_in() ) {
return new WP_Error(
'rest_not_logged_in',
__( 'REST API は認証が必要です', 'mytheme' ),
array( 'status' => 401 )
);
}
return $result;
} );ワナ 5:raw フィールドで HTML が裸で返る
/wp/v2/posts/<id>?context=edit を編集権限ユーザーで叩くと、title.raw / content.raw が 未エスケープで返ります。フロントで描画する場合はサニタイズ必須です。
ワナ 6:HTTPSじゃないと致命的
Application Passwords は HTTPS 必須ですが、Cookie 認証も含めて 平文HTTPで API を運用するのは論外 です。サイト全体を HSTS + HTTPS で固めましょう。
セキュリティ・運用チェックリスト
WordPress REST API を本番投入する前のチェックリスト:
- HTTPS 化(HSTS 含む)
- Application Passwords を使う場合のみ有効化、不要なら無効化フィルタを設定
-
/wp/v2/usersの匿名アクセスを遮断 - カスタムエンドポイントは 必ず
permission_callbackを実装 -
argsでvalidate_callback+sanitize_callbackを両方指定 - CDN/WAF でレート制限
- エラー時は
WP_Errorでstatus付きで返す(500 を出さない) - 重い処理は Transients API + Redis Object Cache でキャッシュ
- フロント側は
_fields/_embedでオーバーフェッチを抑制 - アクセスログ/監査ログを残し、定期レビュー
まとめ
WordPress REST API は、
- WP 4.7 以降コア標準で
/wp-json/wp/v2/から全リソースを操作できる - 認証は 同一サイトJSなら Cookie + X-WP-Nonce、外部から叩くなら Application Passwords が定石
register_rest_route+permission_callback+args(validate/sanitize)がカスタム実装の3点セット- Next.js などと組み合わせてヘッドレスCMS として使えば、WordPressの編集体験 + モダンフロントの速度を両立できる
- 一方で ユーザー列挙・CSRF・レート制限なし などの構造的弱点があり、必ず対策しないと事故る
ヘッドレス WordPress や、外部システム連携、スマホアプリのバックエンドなど、用途は今後も広がり続けます。本記事のチェックリストを横に置きながら、安全で軽量な REST API 活用を進めてください。
参考リンク
- 公式 REST API Handbook:REST API Handbook
- 認証方法:Authentication
- Application Passwords:Application Passwords Reference / Advanced Administration
- カスタムエンドポイント:Adding Custom Endpoints / Routes and Endpoints
- WPGraphQL:wpgraphql.com
- Next.js + WP ヘッドレス:Kinsta 解説