“The response is not a valid JSON response.” “Updating failed.” “Publishing failed.” All three are the same WordPress REST API error wearing different words, and all three have the same short list of causes. This guide walks them in the order that finds the fault fastest, with the commands to prove each one.
What a WordPress REST API error actually means
WordPress’s editor does not post forms. It talks to your site over HTTP, to URLs under /wp-json/, and expects JSON back. When it gets something that is not JSON — an HTML error page, a redirect, an empty body, a firewall’s block page — it reports the only thing it can: the response was not valid JSON.
So a WordPress REST API error is almost never a fault in the API. It is something standing between the editor and the API, returning the wrong thing.
The reframe that saves an hour: stop asking “why is the REST API broken” and start asking “what is answering this URL instead of WordPress”.
Two commands that narrow it down
Before changing a single setting, find out what the endpoint returns. This is the whole diagnosis, and it takes thirty seconds.
# 1. Does the API root answer at all?
curl -sS -o /dev/null -w "%{http_code}\n" https://yoursite.com/wp-json/
# 2. What does it actually return?
curl -sS https://yoursite.com/wp-json/ | head -c 300| What you get | What it means | Go to |
|---|---|---|
200 and JSON | The API is fine. The problem is authenticated requests | Cause 8 |
404 | Rewrite rules or permalinks | Cause 1 |
401 or 403 | Something is blocking it deliberately | Causes 2, 3 |
| HTML, not JSON | A plugin, firewall or error page is answering | Causes 2, 5 |
500 | A plugin or theme is fatalling on the request | Cause 5 |
| Empty response | PHP died. Check the error log | Cause 6 |
A redirect to http:// | Mixed protocol config | Cause 7 |
Whatever that table sends you to, you now know which half of the site to look at. Most of the time people solving a WordPress REST API error skip this step and spend an afternoon disabling plugins at random.
The eight causes, in order
Cause 1 — Permalinks and rewrite rules
The REST API lives on pretty permalinks. If rewrite rules are stale, /wp-json/ 404s while the site itself looks perfectly normal.
# Rewrites the rules; fixes a surprising share of cases
wp rewrite flush --hard
# Confirm permalinks are not set to Plain
wp option get permalink_structureAn empty permalink_structure means Plain permalinks, and the API falls back to the ?rest_route= form. That still works, but plenty of plugins assume the pretty form and this is a very common WordPress REST API error on freshly migrated sites.
Cause 2 — A security plugin blocking it
The big one. Several security plugins offer “disable REST API for unauthenticated users” as a hardening option, and it sounds sensible until the editor stops saving.
# Does the block clear when plugins are out of the way?
curl -sS -o /dev/null -w "%{http_code}\n" "https://yoursite.com/?rest_route=/"
wp plugin deactivate --all --skip-plugins=your-cache-plugin
# retest, then reactivate one at a timeReactivate in order of suspicion — security, firewall, then everything else. Whichever one brings the WordPress REST API error back is your answer, and its setting is the fix, not its removal.
Cause 3 — mod_security or a host-level firewall
Some hosts run rules that reject requests carrying JSON bodies, or any POST to /wp-json/. The tell is that the API root works but saving a post fails.
# A POST, the way the editor makes one
curl -sS -o /dev/null -w "%{http_code}\n" -X POST \
-H "Content-Type: application/json" \
-d '{"title":"test"}' https://yoursite.com/wp-json/wp/v2/postsA 403 here with a 200 on the root is a firewall, not WordPress. Only your host can adjust it — ask them to whitelist /wp-json/ for logged-in requests rather than turning the rules off.
Cause 4 — A caching layer caching the API
The REST API must not be cached. If it is, the editor reads a stale response and reports a WordPress REST API error that changes every time you reload.
# Is a cache answering the API?
curl -sSI https://yoursite.com/wp-json/ | grep -iE 'x-litespeed|x-cache|cf-cache-status|age:'Any cache hit on that URL is a misconfiguration. Exclude /wp-json/ in the cache plugin and at the CDN. We covered the same class of problem in why editor changes do not appear — a cache in the wrong place produces confusing, intermittent faults.
Cause 5 — A plugin or theme fatalling on the request
A 500 or an empty body means PHP stopped. The REST request runs your plugins, so anything that fatals on init will fatal here.
# Turn logging on, reproduce, then read the log
wp config set WP_DEBUG true --raw
wp config set WP_DEBUG_LOG true --raw
wp config set WP_DEBUG_DISPLAY false --raw
tail -40 wp-content/debug.logTurn display off. A notice printed before the JSON is itself enough to cause a WordPress REST API error, because the response is then not valid JSON — which is how a harmless deprecation warning takes down the editor. Our post on headers already sent covers the same output-before-content trap.
Cause 6 — PHP limits
Large requests — a long post, many blocks — can exceed memory or execution limits and die silently.
wp eval 'echo ini_get("memory_limit") . " / " . ini_get("max_execution_time") . "s\n";'If either is low, raise it before concluding anything else. We wrote up the memory error and the timeout separately, because both produce a WordPress REST API error as a side effect rather than as the message.
Cause 6b — A stale .htaccess
Related to cause 1 but distinct, and common after a migration or a cache-plugin change. A cache plugin writes its own block into .htaccess; the plugin is later removed or replaced and the block stays, still rewriting requests that no longer mean anything.
# What is actually in there
head -40 .htaccess
# Regenerate the WordPress block cleanly
wp rewrite flush --hardLeftover rules from a plugin that is no longer installed are one of the least obvious sources of a WordPress REST API error, because nothing in the admin hints at them.
Cause 7 — Site URL and protocol mismatch
If WordPress thinks it is on http and the browser is on https, the editor’s request redirects, and a redirect is not JSON.
wp option get siteurl
wp option get home
# Both should be https, and identical apart from any pathCause 8 — Authentication, not availability
If the API root returns clean JSON to curl but the editor still fails, the request is failing because of who is making it. Cookies, nonces, or a reverse proxy stripping headers.
# Does an authenticated route respond for an anonymous request?
curl -sS https://yoursite.com/wp-json/wp/v2/settings | head -c 120
# A 401 here is CORRECT — it proves auth is being evaluatedA 401 is the healthy answer. If you get HTML instead, something is intercepting authenticated requests specifically, and a proxy stripping the Authorization or cookie header is the usual culprit.
Why “disable the REST API” is the wrong fix
Search the error and you will find advice to switch the REST API off. Do not. The block editor, Site Health, the media library and most modern plugins all run on it. Turning it off to cure a WordPress REST API error is removing the engine to stop a rattle.
The concern behind that advice is real but narrow: by default, unauthenticated requests can list users at /wp-json/wp/v2/users, which helps someone enumerate login names. That is worth closing. The way to close it is to close that, not the API.
# Require authentication for the users endpoint only
add_filter( 'rest_authentication_errors', function ( $result ) {
if ( ! empty( $result ) ) { return $result; }
$route = $GLOBALS['wp']->query_vars['rest_route'] ?? '';
if ( 0 === strpos( $route, '/wp/v2/users' ) && ! is_user_logged_in() ) {
return new WP_Error( 'rest_forbidden', 'Authentication required.', array( 'status' => 401 ) );
}
return $result;
} );That closes the enumeration path and leaves everything else working. Combine it with the rest of a sensible hardening routine and you have the benefit without the WordPress REST API error you would otherwise create.
Authenticated requests specifically
A class of this problem only affects logged-in users, which makes it maddening: the site is fine, curl is fine, and only the editor fails.
| Symptom | Likely cause | Check |
|---|---|---|
| Fails for everyone, always | Blocking or rewrite | curl the root |
| Fails only when logged in | Cookies or nonce | Log out, retest anonymously |
| Fails after ~24 hours idle | Expired nonce | Reload the editor |
| Fails behind a proxy or CDN only | Headers stripped | Bypass the CDN and retest |
| Fails on long posts only | PHP or proxy body limit | Cause 6 |
| Fails on one user account | Roles or a capability plugin | Test with an admin |
The nonce row catches people out regularly. Leave the editor open overnight, come back, hit save, and you get a WordPress REST API error that a page reload cures — because the security token expired while the tab sat there.
A real diagnosis, start to finish
Here is the shape of an actual case, because the sequence matters more than any individual command.
A client reported that publishing had stopped working that morning. Nothing had been updated, nobody had changed a setting, and the site itself was loading perfectly. The message was the familiar WordPress REST API error about an invalid JSON response.
Minute one: curl the root
curl -sS -o /dev/null -w "%{http_code}\n" https://theirsite.com/wp-json/
# 403A 403 to an anonymous request. That single number ruled out half the list immediately: not permalinks, not a plugin fatal, not PHP limits. Something was refusing the request deliberately.
Minute two: see who is refusing
curl -sS https://theirsite.com/wp-json/ | head -c 200
# <html><head><title>403 Forbidden</title>...HTML, not JSON, and an Apache-styled error page rather than anything WordPress produces. WordPress was never reached. That narrowed the WordPress REST API error to the web server or a layer in front of it.
Minute five: ask the host
The host had rolled out a new mod_security rule set overnight, and one rule rejected any request whose path contained wp-json — added as a blanket anti-enumeration measure. Nobody had changed anything on the site because the change was not on the site.
The fix was a support ticket asking for the rule to be relaxed for authenticated requests, and the site was publishing again inside the hour. Had we started by deactivating plugins, we would have spent the morning proving that none of them was the cause.
What made it quick: two curl commands, before touching WordPress at all. Every WordPress REST API error deserves those thirty seconds first.
Keeping it from coming back
Most sites that hit this acquire it from a change someone made deliberately. Four habits stop the repeat.
| Habit | Why |
|---|---|
Exclude /wp-json/ from every cache layer | Cached API responses cause intermittent faults nobody can reproduce |
WP_DEBUG_DISPLAY off in production | One printed notice invalidates the JSON |
| Read what a hardening toggle does before enabling it | “Disable REST API” is the most common self-inflicted cause |
Check /wp-json/ after any migration | Rewrite rules do not always survive the move |
| Keep a note of host-level firewall changes | The cause is often not on the site at all |
| Test as an anonymous visitor, not just logged in | Tells you which half is broken in one command |
Adding the endpoint check to a routine is the cheap version of all of this. If you run a monthly pass over a site — and our maintenance checklist argues you should — one curl line catches a WordPress REST API error before an editor does.
# Add to your monthly checks: should print 200
curl -sS -o /dev/null -w "wp-json: %{http_code}\n" https://yoursite.com/wp-json/After a migration it matters more, not less. Moving a site changes the web server, the rewrite rules and often the firewall in one go, and a WordPress REST API error discovered a fortnight later is much harder to attribute than one found on the day. Our hosting-move guide puts the same check in its post-move list.
Where this goes wrong
| Mistake | What it costs |
|---|---|
| Disabling the REST API | The editor, media library and Site Health |
| Deactivating plugins at random | An afternoon, and no idea which one it was |
Leaving WP_DEBUG_DISPLAY on | The notice itself becomes the error |
Caching /wp-json/ | Intermittent failures nobody can reproduce |
| Assuming it is WordPress | It is usually the host or a plugin |
| Testing only while logged in | You never learn which half is broken |
Notice how many of those involve changing something before knowing what is wrong. The API either answers or it does not, and finding out costs one command — every minute spent guessing is a minute the editor stays broken.
If you take one habit from this: always run the anonymous curl first. A WordPress REST API error that reproduces for curl is an infrastructure problem; one that does not is an authentication problem. Those are two different afternoons, and the command that tells them apart takes five seconds.
Common questions about a WordPress REST API error
Is it safe to leave the REST API enabled?
Yes, and you have little choice — WordPress depends on it. Close the user-enumeration endpoint if you want, and leave the rest alone. The API is documented at developer.wordpress.org.
Why does the classic editor work when the block editor does not?
Because the classic editor posts a form and the block editor uses the API. That difference is the strongest evidence you have: it confirms the WordPress REST API error is about the transport, not about your content.
“Updating failed” but the post saved anyway. What happened?
The request reached WordPress and the response did not get back cleanly — a timeout, or a proxy cutting the connection. Your content is safe; the editor simply never heard the confirmation.
Could my theme cause this?
Yes, if it outputs anything before headers or fatals on init. Switch to a default theme for one test. If the WordPress REST API error disappears, you have found the neighbourhood.
Does this affect the front end?
Usually not. Pages render from PHP and do not need the API. The exception is a theme or plugin that fetches content over the API in the browser — a live search, say — which will be broken too.
Why does it work on my laptop but fail for the client?
Because you are probably not going through the same path. A CDN, a corporate proxy or a country-level firewall can sit in front of one visitor and not another, and each of those can turn a healthy API into a WordPress REST API error for that person only. Ask them to try from a phone on mobile data — if it works there, the fault is on their network, not your site.
Does the error mean my site is hacked?
Almost never. A compromised site usually shows itself in other ways — injected links, unknown admin users, outbound spam. A WordPress REST API error on its own is a plumbing fault. If you have other reasons to be worried, our guide to telling whether a site is compromised lists the signals that actually matter.
I use a page builder. Do I care about this?
Less, but not none. Elementor and Divi have their own save mechanisms, so the block editor failing may not stop you working. The media library, Site Health and many plugins still use the API, so a WordPress REST API error will surface somewhere eventually.
Can a single post cause it?
Yes — usually a very large one, or one containing something a firewall rule dislikes. If every post saves except one, you are looking at a size or content trigger rather than a site-wide WordPress REST API error, and the PHP and proxy body limits in cause 6 are where to start.
How do I stop it happening again?
Exclude /wp-json/ from every cache, keep WP_DEBUG_DISPLAY off in production, and read what a security plugin’s hardening toggle does before enabling it. Most of the sites we see with this problem acquired it from a setting someone turned on to be safe.
