Securing WordPress REST API v2: Complete Protection Guide
Learn how to secure the WordPress REST API v2 from unauthorized access, data leakage, and abuse while maintaining functionality.
The WordPress REST API v2 powers modern WordPress development, but also creates potential security vulnerabilities. This guide covers comprehensive API security strategies to protect your site while maintaining necessary functionality.
Understanding REST API Risks
The REST API exposes endpoints that can reveal sensitive information or allow unauthorized actions if not properly secured. Common risks include user enumeration, content exposure, and authentication bypass attempts.
Default Exposed Endpoints
Out of the box, WordPress exposes:
- /wp-json/wp/v2/users - Lists all users with usernames
- /wp-json/wp/v2/posts - Exposes all published content
- /wp-json/wp/v2/pages - Lists all pages
- /wp-json/wp/v2/comments - Shows comment data
- /wp-json/wp/v2/media - Lists media library items
Disabling User Enumeration
User enumeration through the REST API is a significant security concern:
// Disable REST API user endpoint for non-logged users
add_filter('rest_endpoints', function($endpoints) {
if (!is_user_logged_in()) {
if (isset($endpoints['/wp/v2/users'])) {
unset($endpoints['/wp/v2/users']);
}
if (isset($endpoints['/wp/v2/users/(?P<id>[\d]+)'])) {
unset($endpoints['/wp/v2/users/(?P[\d]+)']);
}
}
return $endpoints;
});
Authentication Requirements
Require authentication for sensitive endpoints:
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',
'You must be logged in to access this endpoint.',
array('status' => 401)
);
}
return $result;
});
Selective Endpoint Protection
Allow public access to specific endpoints while protecting others:
add_filter('rest_authentication_errors', function($result) {
$public_routes = array(
'/wp/v2/posts',
'/wp/v2/pages',
'/contact-form/v1/submit',
);
$current_route = $_SERVER['REQUEST_URI'];
foreach ($public_routes as $route) {
if (strpos($current_route, $route) !== false) {
return $result;
}
}
if (!is_user_logged_in()) {
return new WP_Error('unauthorized', 'Authentication required', array('status' => 401));
}
return $result;
});
Rate Limiting API Requests
Implement rate limiting to prevent API abuse:
add_action('rest_api_init', function() {
$ip = $_SERVER['REMOTE_ADDR'];
$transient_key = 'api_rate_' . md5($ip);
$requests = get_transient($transient_key) ?: 0;
if ($requests > 100) { // 100 requests per minute
wp_send_json_error(
array('message' => 'Rate limit exceeded'),
429
);
}
set_transient($transient_key, $requests + 1, MINUTE_IN_SECONDS);
});
Validating and Sanitizing Input
Always validate API input data:
register_rest_route('custom/v1', '/submit', array(
'methods' => 'POST',
'callback' => 'handle_submission',
'permission_callback' => '__return_true',
'args' => array(
'email' => array(
'required' => true,
'validate_callback' => function($param) {
return is_email($param);
},
'sanitize_callback' => 'sanitize_email',
),
'message' => array(
'required' => true,
'sanitize_callback' => 'sanitize_textarea_field',
),
),
));
CORS Configuration
Control which domains can access your API:
add_action('rest_api_init', function() {
$allowed_origins = array(
'https://yourdomain.com',
'https://app.yourdomain.com',
);
$origin = isset($_SERVER['HTTP_ORIGIN']) ? $_SERVER['HTTP_ORIGIN'] : '';
if (in_array($origin, $allowed_origins)) {
header('Access-Control-Allow-Origin: ' . $origin);
header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
header('Access-Control-Allow-Credentials: true');
}
}, 15);
Monitoring API Activity
Log REST API requests for security monitoring:
add_action('rest_api_init', function() {
$log_entry = array(
'time' => current_time('mysql'),
'ip' => $_SERVER['REMOTE_ADDR'],
'endpoint' => $_SERVER['REQUEST_URI'],
'method' => $_SERVER['REQUEST_METHOD'],
'user' => get_current_user_id(),
);
$logs = get_option('api_access_logs', array());
array_unshift($logs, $log_entry);
$logs = array_slice($logs, 0, 1000);
update_option('api_access_logs', $logs);
}, 1);
Security Headers for API
Add security headers to API responses:
add_filter('rest_post_dispatch', function($response) {
$response->header('X-Content-Type-Options', 'nosniff');
$response->header('X-Frame-Options', 'DENY');
$response->header('Cache-Control', 'no-store, no-cache, must-revalidate');
return $response;
});
Conclusion
Securing the WordPress REST API requires layered protection—authentication, rate limiting, input validation, and monitoring. These measures protect your site while maintaining the functionality modern WordPress applications require.
Written by Sarah Chen
WP Folder Shield Team