- 🔹 1. Check REST Endpoint in the Browser First
- 🔹 2. 404: Route Not Registered
- 🔹 3. 403: Permission Callback Blocking Access
- 🔹 4. 500: PHP Fatal Error Inside the Callback
- 🔹 5. Browser Says “Unexpected token
- 🔹 6. Wrong HTTP Method (GET vs POST)
- 🔹 7. Nonce Required (For Authenticated Requests)
- 🔹 8. CORS Errors (When Calling External APIs)
- 🔹 9. JSON Parse Errors
- 🔹 10. REST Conflicts With Security Plugins
- 🔹 11. REST Blocked by Caching Plugins
- 🎯 Summary
- ✔ 404 → route not registered
- ✔ 403 → invalid permission callback or missing nonce
- ✔ 500 → PHP error inside callback
- ✔ HTML returned instead of JSON
- ✔ JS calling wrong method (GET/POST mismatch)
- ✔ CORS error from external APIs
- ✔ response corrupted by notices/echo/debug output
- ✔ security plugin blocking endpoint
- ✔ caching plugin interfering
Fix 404, 403, 500, JSON, and CORS issues when using REST inside custom or add-on commands.
If your custom command or add-on uses:
fetch('/wp-json/...')
and the terminal prints errors or nothing happens, the issue often comes from REST API configuration, routing, permission callbacks, or caching.
This guide helps diagnose and fix every common REST issue.
🔹 1. Check REST Endpoint in the Browser First #
Before testing inside the terminal, open the URL directly:
Example:
https://yoursite.com/wp-json/myaddon/v1/data
If you see:
- valid JSON → good
- an error message → route exists but failing
{"code":"rest_no_route"}→ route doesn’t exist- white screen → fatal PHP error
🔹 2. 404: Route Not Registered #
Most common error:
{"code":"rest_no_route", "message":"No route was found"}
Causes:
- wrong namespace
- wrong route path
- missing endpoint
- callback not loaded
- plugin inactive
Correct registration example:
register_rest_route('myaddon/v1', '/data', [
'methods' => 'GET',
'callback' => 'myaddon_get_data',
'permission_callback' => '__return_true'
]);
Common mistakes:
❌ declaring route inside a function that never runs
❌ wrong namespace (myaddon vs my-add-on)
❌ using /myaddon/v1/data instead of namespace + route
🔹 3. 403: Permission Callback Blocking Access #
If you get:
{"code":"rest_forbidden", "message":"Sorry, you are not allowed."}
Your permission_callback is the problem.
Fix (for public endpoints):
'permission_callback' => '__return_true'
Fix (for logged-in users only):
'permission_callback' => function() {
return is_user_logged_in();
}
Fix (admin-only):
'permission_callback' => function() {
return current_user_can('manage_options');
}
🔹 4. 500: PHP Fatal Error Inside the Callback #
Console shows:
Failed to load resource: the server responded with a status of 500
To diagnose:
- Enable WP debug:
define('WP_DEBUG', true); define('WP_DEBUG_LOG', true); - Re-run the request
- Check:
/wp-content/debug.log
Common causes:
- undefined variables
- undefined functions
- wrong return type
- missing
wp_send_json() - database errors
Correct callback example:
function myaddon_get_data() {
return [
'success' => true,
'items' => [1,2,3]
];
}
🔹 5. Browser Says “Unexpected token <” or Red Error in Console #
This means your endpoint is returning HTML, not JSON.
Cause examples:
- Fatal error before JSON output
- Theme printing HTML
- Security plugin printing warnings
- A plugin redirecting the request
Check the response in Network → Preview.
If you see:
<html>...
Something is injecting HTML into your response.
Fix:
- disable security plugins temporarily
- ensure no
echoorprint_rin callback - disable maintenance mode plugins
🔹 6. Wrong HTTP Method (GET vs POST) #
Your route may define:
'methods' => 'POST'
But your JS uses:
fetch('/wp-json/myaddon/v1/data')
→ Default fetch is GET → mismatch → fails.
Solution:
Match the method:
fetch('/wp-json/myaddon/v1/data', {
method: 'POST',
body: JSON.stringify({...}),
headers: { 'Content-Type': 'application/json' }
});
OR change the route to:
'methods' => 'GET'
🔹 7. Nonce Required (For Authenticated Requests) #
If your endpoint requires the user to be logged in, you must include a nonce:
wp_localize_script('my-addon', 'MyAddOnData', [
'nonce' => wp_create_nonce('wp_rest')
]);
JavaScript:
fetch('/wp-json/myaddon/v1/secure', {
headers: {
'X-WP-Nonce': MyAddOnData.nonce
}
});
Otherwise you’ll get:
403 forbidden (missing nonce)
🔹 8. CORS Errors (When Calling External APIs) #
If your JS calls external services like:
https://api.blockchain.com/
https://some-other-site.com/
And the browser shows:
CORS policy error
This cannot be fixed from the terminal plugin.
Options:
- Use a server-side proxy REST route
- Contact the API provider to enable CORS
- Use a WordPress-side
wp_remote_get()
Example proxy:
function myaddon_proxy() {
$res = wp_remote_get('https://external-api.com/data');
return json_decode(wp_remote_retrieve_body($res), true);
}
🔹 9. JSON Parse Errors #
If the terminal prints:
Unexpected end of JSON input
or:
Invalid JSON response
Your endpoint is outputting:
- blank output
- whitespace
- BOM encoding
- HTML warnings
- PHP notices
Check response in Network → Response.
Fix:
- ensure callback returns pure array
- no echoes
- no HTML
- no error messages
🔹 10. REST Conflicts With Security Plugins #
Plugins like:
- Wordfence
- Defender
- Sucuri
- Ninja Firewall
- iThemes Security
…may block custom REST routes.
Fix:
- whitelist URIs under the plugin’s firewall settings
- disable “block REST API for guests”
- disable “REST API hardening” temporarily
🔹 11. REST Blocked by Caching Plugins #
Page caching plugins may:
- cache REST responses
- merge JS that performs requests
- block REST for anonymous visitors
- rewrite URLs
- break authentication
Fix:
- Exclude
/wp-json/from cache - Disable REST API caching (if plugin has it)
- Turn off JS combine/minify for add-on scripts
🎯 Summary #
REST issues usually fall into one of these categories:
✔ 404 → route not registered #
✔ 403 → invalid permission callback or missing nonce #
✔ 500 → PHP error inside callback #
✔ HTML returned instead of JSON #
✔ JS calling wrong method (GET/POST mismatch) #
✔ CORS error from external APIs #
✔ response corrupted by notices/echo/debug output #
✔ security plugin blocking endpoint #
✔ caching plugin interfering #
Once the route loads properly and the callback returns clean JSON, commands perform reliably.