最早开发小半WordPress ai助手的时候并不是用的REST API服务,而是全程用的AJAX处理,包括以前的插件也都是AJAX处理,毕竟没啥大功能,但是ai助手不行。
特别是要支持流式输出,如果用AJAX处理,会中断卡死,最后就选择了REST API服务。
AJAX处理相对更适合小功能,不频繁处理以及后台的功能,AJAX每次处理的时候,会重载WordPress的核心环境,且无法做缓存,如果做了缓存功能可能就失效了,这对小型服务器来说,也是一种负担。
用REST API服务虽然每次请求都会重载整个网站,但是REST API的请求可以走缓存。
只是网站开启了REST API服务,就会暴露很多REST相关的页面,特别是用户页面users,这个算低危漏洞,毕竟暴露网站用户名了,如果你网站要做公安备案,可能会收到官方的漏洞通知,所以我们提前做下屏蔽。
所以用了REST API服务,就需要手动进行屏蔽,把一些不想暴露的页面给屏蔽了。
对比总结
| 方面 | REST API | Admin AJAX |
|---|---|---|
| 安全性 | 内置权限控制,需手动屏蔽公开端点 | 手动验证,默认内部 |
| 速度 | 支持缓存,响应快 | 简单请求快,缓存不友好 |
| 性能 | 加载轻量,高并发友好 | 加载较重,高并发可能瓶颈 |
| 扩展性 | 优秀,适合现代架构 | 有限,适合小型功能 |
| 兼容性 | WordPress 4.4+ | 几乎所有版本 |
| 适用场景 | 前后端分离 | 后台功能 |
REST API服务安全防护
我几个网站功能不同,开放权限也都不一样,所以配置不同,看着有点懵圈,我自己配置的时候都混乱了。
更新:2025.09.25 做下简化,我自己都有点乱,因为不同的网站都用了不一样的方案,我只是给你们参考,不确定的去问下ai吧。
1、如果你没有需要公开的数据,可以直接仅登录用户才能使用REST API服务。
把代码加到主题的functions.php文件里面:
add_filter('rest_authentication_errors', function($result) {
if (!is_user_logged_in()) {
return new WP_Error('function_disabled', 'This feature is not available.', array('status' => 401));
}
return $result;
});
2、如果你有需要公开的数据,可以屏蔽重点页面。
比如在用Gutenberg编辑器、小程序、app之类的。
屏蔽media、settings、users这3个页面以及动态?rest_route=media、settings、users对应的3个页面。
服务器规则来限制,nginx配置:
location ~ ^/wp-json/wp/v2/(media|settings|users) {
deny all;
return 403;
}
不推荐在nginx规则里面用if,所以屏蔽动态页面的直接在php端来实现:
// 拦截 ?rest_route= 形式的请求
add_action('parse_request', function($wp) {
// 仅处理包含 rest_route 参数的请求
if (empty($_GET['rest_route'])) {
return;
}
$rest_route = trim($_GET['rest_route'], '/');
$blocked_routes = [
'wp/v2/users',
'wp/v2/users/(?P<id>[\\d]+)',
'wp/v2/media',
'wp/v2/settings'
];
foreach ($blocked_routes as $route) {
if (preg_match('#^' . str_replace(['\d+', '\w+'], ['[0-9]+', '[a-zA-Z0-9_-]+'], preg_quote($route, '#')) . '(/|$)#', $rest_route)) {
status_header(403);
wp_die('REST API 访问被禁止', 'Forbidden', ['response' => 403]);
exit;
}
}
}, 1);
把这个放到主题的functions.php文件里面就行。
3、如果你需要公开指定页面端口,其他屏蔽。
比如只开放文章和分类页面,但是其他页面全部屏蔽
# 允许 /wp-json/wp/v2/posts 及其子路由和categories路由
location ~* ^/wp-json/wp/v2/(posts(/.*)?|categories)$ {
try_files $uri $uri/ /index.php?$args;
}
# 屏蔽其他页面
location ~* ^/wp-json(/.*)?$ {
deny all;
return 403;
}
这是改nginx配置文件。
4、屏蔽全部默认的v2页面数据,并且禁止直接访问 /wp-json 和 /wp-json/页面:
nginx配置:
# 禁止直接访问 /wp-json 和 /wp-json/
location = /wp-json {
return 301 $scheme://$host/;
}
location = /wp-json/ {
return 301 $scheme://$host/;
}
# 禁止直接访问 ?rest_route=/ 和 ?rest_route=
location ~* \?rest_route=(/)?$ {
return 301 $scheme://$host/;
}
# 屏蔽 WordPress 默认 REST API 端点(wp/v2 和 oembed/1.0)
location ~* ^/wp-json/(wp/v2|oembed/1.0)(/.*)?$ {
deny all;
return 403;
}
# 其他 REST API 请求交给 WordPress 处理
location ~* ^/wp-json(/.*)?$ {
try_files $uri $uri/ /index.php?$args;
}
动态处理:
// 专门拦截 ?rest_route= 形式的请求
add_action('parse_request', function($wp) {
if (empty($_GET['rest_route'])) {
return;
}
$rest_route = trim($_GET['rest_route'], '/');
if (preg_match('#^(wp/v2|oembed/1.0)(/.*)?$#', $rest_route)) {
header('Content-Type: application/json');
status_header(403);
echo json_encode([
'code' => 'function_disabled',
'message' => 'REST API 访问被禁止',
'data' => ['status' => 403]
]);
exit;
}
}, 1);
放functions.php文件里面。
nginx是管/wp-json/页面的,WordPress的php代码,也就是functions.php文件是管带问号的$rest_route这种动态页面。
根据你自己的需求来,如果不用就直接关闭这个功能。
直接关闭:
add_filter('rest_enabled', '__return_false');
add_filter('rest_jsonp_enabled', '__return_false');
这个也是可以放到functions.php文件里面。
现在很多主题或者插件都自带关闭REST API服务的功能。
我现在的方案:
1、未登录的用户只开放文章和分类页面,其他一律屏蔽。
wordpress层级代码:
// 未登录开放指定页面
add_filter('rest_authentication_errors', function($result) {
// 获取当前 REST 路由
$rest_route = isset($_GET['rest_route']) ? $_GET['rest_route'] : '';
if (empty($rest_route)) {
// 处理 /wp-json/ 请求
$rest_route = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$rest_route = str_replace('/wp-json', '', $rest_route);
}
// 允许公开访问 /wp/v2/posts 及其子路由
if (strpos($rest_route, '/wp/v2/posts') === 0) {
return $result;
}
// 允许公开访问 /wp/v2/categories(精确匹配)
if ($rest_route === '/wp/v2/categories') {
return $result;
}
// 其他端点要求登录
if (!is_user_logged_in()) {
return new WP_Error('function_disabled', 'This feature is not available.', array('status' => 401));
}
return $result;
});
nginx配置:
# 允许 /wp-json/wp/v2/posts 及其子路由
location ~* ^/wp-json/wp/v2/posts(/.*)?$ {
try_files $uri $uri/ /index.php?$args;
}
# 只允许 /wp-json/wp/v2/categories
location = /wp-json/wp/v2/categories {
try_files $uri $uri/ /index.php$is_args$args;
}
location ~* ^/wp-json(/.*)?$ {
deny all;
return 403;
}
这个表示:
- 只可以正常获取到文章内容和文章分类
- 其他页面一律不能访问,直接403提示。
这个网站是企业官网,文章和分类是放到首页的,给用户看最新动态啥的,其他都用不到了,这个首页是一个独立的页面,所以才用了这种方式。
2、默认屏蔽所有自带页面数据(/wp/v2),对未登录用户开放指定端口,剩余端口需要登录访问。
wordpress层级代码:
// 控制 REST API 访问(统一处理 wp-json 和 ?rest_route= 两种情况)
add_filter('rest_authentication_errors', function($result) {
// 获取当前 REST 路径
if (!empty($GLOBALS['wp']->query_vars['rest_route'])) {
$current_route = trim($GLOBALS['wp']->query_vars['rest_route'], '/');
} elseif (!empty($_GET['rest_route'])) {
$current_route = trim($_GET['rest_route'], '/');
} else {
$current_route = '';
}
error_log("REST current_route: " . $current_route);
// 允许未登录访问的命名空间
$allowed_patterns = [
'#^端口1/v1(/.*)?$#',
'#^端口2/v1(/.*)?$#',
'#^端口3/v1(/.*)?$#'
];
foreach ($allowed_patterns as $pattern) {
if (preg_match($pattern, $current_route)) {
error_log("REST route allowed: $current_route");
return true; // 直接返回 true
}
}
// 屏蔽默认端点和根路径
if ($current_route === '' || preg_match('#^(wp/v2|oembed/1.0)(/.*)?$#', $current_route)) {
return new WP_Error('rest_forbidden', 'This feature is not available.', ['status' => 403]);
}
// 其他未匹配端口,未登录用户返回 401
if (!is_user_logged_in()) {
error_log("REST API blocked: Route $current_route not allowed for unauthenticated users");
return new WP_Error('rest_not_logged_in', '您必须登录才能访问此资源。', ['status' => 401]);
}
return $result;
}, 10, 1);
nginx配置:
# 禁止直接访问 /wp-json 和 /wp-json/
location = /wp-json {
return 301 $scheme://$host/;
}
location = /wp-json/ {
return 301 $scheme://$host/;
}
# 禁止 ?rest_route=/ 和 ?rest_route=
location ~* \?rest_route=(/)?$ {
return 301 $scheme://$host/;
}
# 允许特定的插件 REST API 端点
# location ~* ^/wp-json/(自定义端口1/v1(/.*)?|自定义端口2/v1(/.*)?)$ {
# try_files $uri $uri/ /index.php?$args;
# }
#允许特定端口这段可以不用
# 屏蔽 WordPress 默认 REST API 端点(wp/v2 和 oembed/1.0)
location ~* ^/wp-json/(wp/v2|oembed/1.0)(/.*)?$ {
deny all;
return 403;
}
# 其他 REST API 请求交给 WordPress 处理
location ~* ^/wp-json(/.*)?$ {
try_files $uri $uri/ /index.php?$args;
}
这个表示:
- 屏蔽默认的wp/v2|oembed/页面,就算已经登录的用户也不能访问。
- 允许没有登录的用户使用自定义的端口请求
- 其他自定义端口都需要登录才能访问
- 禁止直接访问/wp-json、 /wp-json/、?rest_route=/ 和 ?rest_route=页面
这个网站是一个功能服务站点,部分功能对游客进行了开放。
3、禁止所有未登录用户访问,已登录用户也不能访问默认v2端口
WordPress层级:
add_filter('rest_authentication_errors', function($result) {
if (!is_user_logged_in()) {
return new WP_Error('function_disabled', 'This feature is not available.', array('status' => 401));
}
return $result;
});
nginx配置:
# 禁止直接访问 /wp-json 和 /wp-json/
location = /wp-json {
return 301 $scheme://$host/;
}
location = /wp-json/ {
return 301 $scheme://$host/;
}
# 禁止 ?rest_route=/ 和 ?rest_route=
location ~* \?rest_route=(/)?$ {
return 301 $scheme://$host/;
}
# 屏蔽 WordPress 默认 REST API 端点(wp/v2 和 oembed/1.0)
location ~* ^/wp-json/(wp/v2|oembed/1.0)(/.*)?$ {
deny all;
return 403;
}
这个就表示:
- 禁止未登录的用户访问?rest_route相关页面以及其他自定义端口
- 禁止所有用户访问默认的wp/v2 和 oembed/1.0端口
- 已登录用户可以访问自定义端口
- 禁止直接访问/wp-json、/wp-json/、?rest_route=/ 和 ?rest_route=
这个网站是有一些自定义的插件,需要开放,但是其他默认的都不需要开放。
4、未登录的用户开放文章和分类页面,登录用户访问指定端口,其他一律屏蔽。
WordPress层级代码:
// 控制 REST API 访问
add_filter('rest_authentication_errors', function($result) {
// 获取标准化的 REST 路由
$current_route = untrailingslashit($GLOBALS['wp']->query_vars['rest_route'] ?? '');
// 允许未登录用户访问的端点
$allowed_routes = [
'/wp/v2/posts',
'/wp/v2/categories',
];
$allowed_patterns = [
'#^/wp/v2/posts/[^/]+$#' // 支持 /wp/v2/posts/{id}
];
// 检查是否公开访问
if (in_array($current_route, $allowed_routes) || preg_match($allowed_patterns[0], $current_route)) {
return $result;
}
// 其他端点要求登录
if (!is_user_logged_in()) {
return new WP_Error('function_disabled', 'This feature is not available.', array('status' => 401));
}
return $result;
});
// 拦截 ?rest_route= 形式的请求
add_action('parse_request', function($wp) {
if (empty($_GET['rest_route'])) {
return;
}
$rest_route = trim($_GET['rest_route'], '/');
// 允许公开访问的路由
$allowed_routes = [
'wp/v2/posts',
'wp/v2/categories',
];
$allowed_patterns = [
'#^wp/v2/posts/[^/]+$#' // 支持 ?rest_route=/wp/v2/posts/{id}
];
// 检查是否公开访问
if (in_array($rest_route, $allowed_routes) || preg_match($allowed_patterns[0], $rest_route)) {
return;
}
// 屏蔽默认端点和其他端点
if ($rest_route === '' || preg_match('#^(wp/v2|oembed/1.0)(/.*)?$#', $rest_route)) {
header('Content-Type: application/json');
status_header(403);
echo json_encode([
'code' => 'function_disabled',
'message' => 'This feature is not available.',
'data' => ['status' => 403]
]);
exit;
}
// 其他端点要求登录
if (!is_user_logged_in()) {
header('Content-Type: application/json');
status_header(401);
echo json_encode([
'code' => 'function_disabled',
'message' => 'This feature is not available.',
'data' => ['status' => 401]
]);
exit;
}
}, 1);
nginx配置:
# 禁止直接访问 /wp-json 和 /wp-json/
location = /wp-json {
return 301 $scheme://$host/;
}
location = /wp-json/ {
return 301 $scheme://$host/;
}
# 禁止 ?rest_route=/ 和 ?rest_route=
location ~* \?rest_route=(/)?$ {
return 301 $scheme://$host/;
}
# 允许公开访问的端点
location ~* ^/wp-json/wp/v2/(posts(/.*)?|categories)$ {
try_files $uri $uri/ /index.php?$args;
}
# 允许自定义端点(需登录)
location ~* ^/wp-json/deepseek/v1(/.*)?$ {
try_files $uri $uri/ /index.php?$args;
}
location ~* ^/wp-json(/.*)?$ {
default_type application/json;
return 403;
}
这个表示:
- 文章和分类是公开的,游客也可以访问
- 其中自定义端口deepseek/v1允许登录访问
- 并且禁止直接访问/wp-json 、 /wp-json/、?rest_route=/ 和 ?rest_route=
- 其他的一律禁止,403错误。
根据自己的需求,问清楚ai怎么写,让它写好全部规则就行。
