2026年5月20日
2026年5月20日
WordPress の REST API を無効化してはいけない理由と、未ログインからのアクセスを制限する方法(ユーザー一覧の非公開・IP 許可)
WordPress の REST API(/wp-json/)は、ブロックエディターや多くのプラグインが使う仕組みです。公式の REST API ハンドブックも「REST API を無効にしてはいけない。管理画面の機能が壊れる」としています。この記事では、無効化ではなく、未ログインの利用者に見せる範囲を絞る方法を説明します。
コードの確認は2026年10月時点の WordPress 6.x のソースと公式ドキュメントに基づいています。
未ログインで何が見えるのか
既定の状態で、ログインしていない人が取得できる主な情報は次のとおりです。
| エンドポイント | 未ログインで取得できる内容 |
|---|---|
/wp-json/wp/v2/posts・/pages |
公開済みの投稿・固定ページ(下書きや非公開は出ない) |
/wp-json/wp/v2/users |
公開記事を書いたユーザーの id・name(表示名)・slug・url・description・アバター画像のURL |
/wp-json/ |
サイト名や登録されているエンドポイントの一覧 |
よく誤解されますが、メールアドレスとログイン名は既定では出ません。フィールド名でいうと email と username で、この2つは edit コンテキスト専用のフィールドで、list_users 権限のないリクエストでは context=edit を指定してもエラーになります。また、未ログインのリクエストでは、一覧に出るのは公開記事を持つユーザーだけです。
ただし slug(ユーザーのニックネームから作られる URL 用の文字列)は、ログイン名と同じ値になっていることがよくあります。ログイン画面への総当たり攻撃の手がかりを減らしたい場合は、ユーザー一覧を隠す意味があります。
コメントについては、REST API からの未ログインのコメント投稿は既定で拒否されます(rest_allow_anonymous_comments フィルターの既定値が false で、rest_comment_login_required の 401 が返る)。このため、REST API 経由のスパムコメント対策として独自の制限を足す必要は通常ありません。
方法1:未ログインのリクエストをすべて拒否する
公開サイトのフロントエンドで REST API を使っていない場合に向く方法です。公式ハンドブックの FAQ にある書き方をもとにしています。テーマの functions.php か、独自プラグインに書きます。
<?php
add_filter( 'rest_authentication_errors', function ( $result ) {
// 既に認証に成功している、または別のエラーがあるときはそのまま返す
if ( true === $result || is_wp_error( $result ) ) {
return $result;
}
// ログインしていなければ 401 を返す
if ( ! is_user_logged_in() ) {
return new WP_Error(
'rest_not_logged_in',
'このAPIはログインが必要です。',
array( 'status' => 401 )
);
}
return $result;
} );
ログインした状態の管理画面(ブロックエディターなど)は、Cookie とノンスで認証されるため、この設定の影響を受けません。外部のアプリやサーバーから使う場合は、アプリケーションパスワードなどで認証すれば通ります。
注意が必要なのは、ログインしていない訪問者のブラウザーから REST API を呼ぶ機能です。たとえば Contact Form 7 は送信処理に REST API を使うため、この方法をそのまま使うとフォームが送れなくなります。導入後は、問い合わせフォーム・検索・無限スクロールなど、表側の機能を一通り試してください。
一部の名前空間だけ未ログインでも通したい場合は、リクエストされたルートを見て除外します。
<?php
add_filter( 'rest_authentication_errors', function ( $result ) {
if ( true === $result || is_wp_error( $result ) ) {
return $result;
}
if ( is_user_logged_in() ) {
return $result;
}
// 未ログインでも許可する名前空間
$allowed = array( '/contact-form-7/v1/' );
$route = isset( $GLOBALS['wp']->query_vars['rest_route'] )
? $GLOBALS['wp']->query_vars['rest_route']
: '';
foreach ( $allowed as $prefix ) {
if ( 0 === strpos( $route, $prefix ) ) {
return $result;
}
}
return new WP_Error( 'rest_not_logged_in', 'このAPIはログインが必要です。', array( 'status' => 401 ) );
} );
REST API へのリクエストは、パーマリンク設定に関係なく rest_route というクエリ変数でルートが渡されます(/wp-json/contact-form-7/v1/... でも ?rest_route=/contact-form-7/v1/... でも同じ値になります)。許可する名前空間は、実際に使っているプラグインに合わせて書き換えてください。
方法2:ユーザー一覧のエンドポイントだけ隠す
ほかの API はそのままにして、ユーザー情報だけ未ログインの利用者から隠す方法です。rest_endpoints フィルターで、登録済みのルートを取り除きます。
<?php
add_filter( 'rest_endpoints', function ( $endpoints ) {
if ( is_user_logged_in() ) {
return $endpoints;
}
unset( $endpoints['/wp/v2/users'] );
unset( $endpoints['/wp/v2/users/(?P<id>[\d]+)'] );
return $endpoints;
} );
ルートのキーは WordPress 本体のユーザーコントローラーが登録する文字列と一致させる必要があります。取り除いたあと、ログアウトした状態で次のように確かめます。
curl -s -o /dev/null -w "%{http_code}\n" https://example.com/wp-json/wp/v2/users
# → 404(rest_no_route)になればよい
ブロックエディターの投稿者の選択欄などは、ログインした状態でこのエンドポイントを使うので、上のように is_user_logged_in() で分けておきます。
なお、ユーザーの slug は REST API 以外でも分かります。投稿者アーカイブ(/author/slug/)や、/?author=1 から投稿者アーカイブへのリダイレクトがその例です。ログイン名を推測されにくくしたいなら、ユーザーの「ニックネーム」と「ブログ上の表示名」をログイン名と別にしておくのが基本の対策です。
方法3:特定のIPアドレスからだけ未ログインで許可する
ヘッドレス構成で、別サーバーのフロントエンドから認証なしでデータを取得している場合などに使えます。
<?php
add_filter( 'rest_authentication_errors', function ( $result ) {
if ( true === $result || is_wp_error( $result ) ) {
return $result;
}
if ( is_user_logged_in() ) {
return $result;
}
// 未ログインでも許可するIPアドレス(例はドキュメント用のアドレス)
$allowed_ips = array( '192.0.2.10', '198.51.100.20' );
$remote_ip = isset( $_SERVER['REMOTE_ADDR'] ) ? $_SERVER['REMOTE_ADDR'] : '';
if ( in_array( $remote_ip, $allowed_ips, true ) ) {
return $result;
}
return new WP_Error( 'rest_forbidden_ip', 'このAPIにはアクセスできません。', array( 'status' => 403 ) );
} );
REMOTE_ADDR は、WordPress に直接つないできた相手のアドレスです。CDN やロードバランサー、リバースプロキシを挟んでいる場合は、その中継機器のアドレスになります。その場合は Web サーバー側(Nginx の real_ip モジュールや Apache の mod_remoteip)で、信頼できる中継元からのヘッダーだけを使って本来のアドレスに置き換えてから判定してください。X-Forwarded-For ヘッダーを PHP でそのまま信用すると、送信者が自由に書き換えられるため制限になりません。
REST APIのURLをHTMLから出さない
WordPress は、ページの <head> 内の <link rel="https://api.w.org/">、HTTP レスポンスの Link ヘッダー、RSD に REST API の URL を出力します。これを止めるには、本体が登録しているアクションを外します。
<?php
remove_action( 'wp_head', 'rest_output_link_wp_head', 10 );
remove_action( 'template_redirect', 'rest_output_link_header', 11 );
remove_action( 'xmlrpc_rsd_apis', 'rest_output_rsd' );
これは URL の案内をやめるだけで、REST API 自体は動き続けます。/wp-json/ は WordPress の標準の場所なので、隠しても見つけにくくなる効果は小さいと考えてください。
Nginxでリクエスト数を制限する
大量のリクエストでサーバーに負荷をかけられるのを防ぐには、Web サーバー側の流量制限が使えます。limit_req_zone は http ブロックに、limit_req は server 内の location に書きます。
# http { } 内
limit_req_zone $binary_remote_addr zone=wp_rest:10m rate=5r/s;
# server { } 内
location /wp-json/ {
limit_req zone=wp_rest burst=10 nodelay;
try_files $uri $uri/ /index.php?$args;
}
location /wp-json/ を新しく作ると、そのパスは既存の location / の try_files を通らなくなるため、同じ try_files を書いておかないと 404 になります。また、/?rest_route=/wp/v2/posts の形式でも REST API は呼べるので、パスでの制限はこの形式を対象にしません。設定を変えたら sudo nginx -t で構文を確かめてから再読み込みします。
導入前に確認すること
- ブロックエディター、メディアの追加、プラグインの設定画面がログインした状態で動くこと
- 問い合わせフォーム、検索、カートなど、ログインしていない訪問者が使う機能が動くこと
- スマートフォンアプリや外部サービス(ヘッドレスのフロントエンド、連携ツール)が REST API を使っていないか
- WooCommerce など、独自のエンドポイントを持つプラグインの公式ドキュメントにある要件
どこまで制限するか迷う場合は、ユーザー一覧だけを隠す方法2から始め、ほかの API は必要に応じて制限を足すのが影響の少ない進め方です。