WordPress PHP source code on a dark editor screen, illustrating REST API debugging and development

WordPress REST API Disabled? 5 Causes and Fixes

Your external tool or plugin cannot connect to WordPress. Every request to /wp-json/ comes back 404, 401, or 403. You have checked the credentials twice, reinstalled the plugin, and searched every forum thread that ends without a resolution. The WordPress REST API being disabled or returning errors is one of the most common integration blockers, and the cause is rarely where you look first. This guide walks through five causes in diagnostic order, from the most common to the most obscure, with exact curl commands to confirm each one and real code to fix it.

We have hit every one of these failures while building ClearPost, which connects to WordPress sites through the REST API. The diagnostic order below mirrors how we troubleshoot when a connection fails. If you are also debugging authentication specifically, our application password troubleshooting guide covers the auth layer in detail. This guide focuses on the broader question: is the REST API itself reachable, or is something blocking it before WordPress even processes your request?

Is REST Actually Disabled, or Is Auth Failing?

Before you fix anything, you need to know which layer is broken. The WordPress REST API can fail at five distinct layers, and each requires a different fix. The single most common mistake is treating a 401 authentication failure as a “REST API is disabled” problem. It is not. The REST API is likely working fine. Your credentials are not arriving at PHP.

Here is how to tell the difference. If GET requests to /wp-json/wp/v2/posts return published posts without authentication (which they should, since reads are public), the REST API is enabled and routing correctly. If authenticated POST or PUT requests fail with 401, you have an authentication problem, not a disabled API. If GET requests to /wp-json/ itself return 404, the REST API is not routing at all, and the problem is at the server or permalink layer. If all requests return 403, something is actively blocking access, either a security plugin, a server rule, or a WAF.

The diagnostic table below maps your symptom to the likely cause and where to jump in this guide.

SymptomHTTP StatusLikely CauseJump To
GET /wp-json/ returns 404404Permalinks not set, rewrite rules missingCause 3
GET works, POST/PUT fails401Authorization header stripped by serverCause 4 or Auth Guide
All requests return 403403Security plugin or rest_authentication_errors filterCause 1 or 2
Everything fails, even GET /wp-json/403 or 503Host-level WAF or server rule blocking /wp-json/Cause 4 or 5
Works in curl, fails in browser200 then blockedCORS headers missing or restrictiveCause 4
Worked before installing a plugin401 or 403Security plugin blocking RESTCause 1

Testing Your REST API With curl

curl isolates the WordPress REST API from any client library, plugin, or wrapper that might be introducing its own issues. Run these commands in order. Each one tells you something specific about which layer is broken.

Step 1: Verify the REST API is reachable

curl -i https://example.com/wp-json/

You should get HTTP 200 with a JSON response listing available namespaces. Look for the authentication key in the response. If it contains an application-passwords object, Application Passwords are available. If the authentication key is empty or missing, Application Passwords are disabled, which usually means the site is not on HTTPS or a filter is blocking them.

If this returns 404, the REST API is not routing. Jump to Cause 3 (permalink and rewrite problems). If it returns 403, something is actively blocking access. Jump to Cause 1 (security plugins) or Cause 5 (host-level WAF).

Step 2: Verify authentication is working

curl -i -u "your_username:your_app_password" https://example.com/wp-json/wp/v2/users/me

If this returns the user object with the correct ID, authentication is working and the problem is capability-based. If it returns 401 with rest_cannot_view_user or user ID 0, authentication is failing. The most common cause is the server stripping the Authorization header before it reaches PHP. For the full diagnostic path on authentication failures, see our guide on fixing the rest_cannot_create error, which covers header stripping, HTTPS requirements, and role permissions in depth.

Step 3: Test a write operation

curl -i -u "your_username:your_app_password" 
 -X POST 
 -H "Content-Type: application/json" 
 -d '{"title":"Test Post","status":"draft"}' 
 https://example.com/wp-json/wp/v2/posts

If this succeeds with HTTP 201, the full pipeline is working: routing, authentication, and capabilities. If it fails with 401, authentication is broken. If it fails with 403, the user lacks the edit_posts capability. If it fails with 404, the route is not registered or permalinks need flushing.

Step 4: Test with an explicit Authorization header

curl -i -H "Authorization: Basic $(echo -n 'your_username:your_app_password' | base64)" 
 https://example.com/wp-json/wp/v2/users/me

If Step 2 failed but this succeeds, the issue was curl’s -u flag. If both fail, the Authorization header is not reaching PHP. The fix depends on your server type, covered in the application password guide linked above.

Cause 1: Security Plugins Disabling REST Endpoints

Security software dashboard showing network protection status, virus-free checks, and app update indicators

Security plugins are the most common cause of REST API failures. They can block requests before WordPress processes them, restrict access to specific endpoints, or disable the REST API entirely. The frustrating part: everything on your end is correct, and the failure is silent. No error message in your client, no log entry that points to the plugin.

Wordfence

Wordfence can block REST API requests through two mechanisms. First, the Web Application Firewall can flag POST or PUT requests containing HTML or JSON payloads as suspicious and return 403 before WordPress processes them. Second, Wordfence’s rate limiting can block requests from an IP making rapid sequential API calls, which is exactly what automation tools do.

Symptom: Requests fail with 403 after installing or reconfiguring Wordfence. GET requests may still work while POST and PUT fail, because the WAF inspects request bodies and writes have larger payloads.

How to confirm: Temporarily deactivate Wordfence and retry your request. If it succeeds, Wordfence is the cause. For a more targeted check, look in Wordfence > Tools > Live Traffic for blocked requests matching your API call. The block entry shows the reason and the rule that triggered it.

The fix: Add the IP address of your automation tool or server to the allowlist under Wordfence > All Options > Advanced Firewall Options > Allowlisted IP addresses. Alternatively, switch the WAF to Learning Mode temporarily (Wordfence > Manage WAF > Web Application Firewall Status > Learning Mode), make your API calls, then switch back to Enabled and Protecting. This trains the WAF to recognize your legitimate requests.

Solid Security (formerly iThemes Security)

Solid Security can disable the REST API or restrict it to authenticated users only. Under Security > Settings > WordPress Tweaks, there is a REST API section with options to restrict API access. If “Restrict REST API access” is enabled, unauthenticated requests are blocked, and depending on the configuration, even authenticated requests may be filtered.

Symptom: All REST requests fail with 401, including unauthenticated GET requests that should be public.

How to confirm: Check the WordPress Tweaks section in Solid Security settings. Temporarily disabling the plugin and retesting is the fastest confirmation.

The fix: Set REST API access to “Open” or ensure your application password requests are not caught by a broader restriction. If you need to restrict access for security, restrict specific namespaces rather than blocking the entire API.

Really Simple Security and All In One WP Security

Really Simple Security has a “Disable user enumeration” option that can block requests to /wp-json/wp/v2/users, which many integration tools depend on. The option is meant to prevent username harvesting but has the side effect of breaking legitimate API integrations that list users. All In One WP Security offers a similar restriction under its REST API settings, blocking requests from non-logged-in users. The problem: “logged in” refers to cookie-based authentication, not Basic Auth via application passwords. Your application password request is treated as a non-logged-in user and gets blocked.

Symptom: Most endpoints work, but /wp-json/wp/v2/users or /wp-json/wp/v2/users/me returns 401 even with valid credentials.

How to confirm: Disable the “Disable user enumeration” option (Really Simple Security) or the REST API restriction (All In One WP Security) and retest. If the request succeeds, that restriction was the blocker.

The fix: Disable the user enumeration restriction for REST API requests, or whitelist the specific endpoints your integration needs. If the plugin cannot be configured to allow authenticated REST access, you may need to deactivate it or find an alternative security plugin that respects application password authentication.

Cause 2: The rest_authentication_errors Filter

A rest_authentication_errors filter in your theme, a plugin, or a must-use plugin can reject REST API requests before they reach any endpoint logic. This filter is the official WordPress mechanism for requiring authentication on REST API requests, replacing the deprecated rest_enabled filter since WordPress 4.7. If the filter returns a WP_Error, every unauthenticated REST request fails. The critical issue: if the filter callback checks is_user_logged_in(), it will reject application password requests because application passwords authenticate via Basic Auth headers, not cookies, and is_user_logged_in() returns false for them.

Symptom: All REST requests return 401 with rest_not_logged_in as the error code. Unauthenticated GET requests that should be public also fail. This tells you the filter is rejecting everything, not just writes.

How to confirm: Search your codebase for the filter:

grep -r "rest_authentication_errors" wp-content/

Check your active theme’s functions.php, any mu-plugins in wp-content/mu-plugins/, and active plugin files. Look for add_filter( 'rest_authentication_errors', ... ) callbacks. The WordPress REST API Handbook documents this filter as the recommended way to restrict API access, but the implementation details matter enormously.

The fix: The filter callback must pass through any existing authentication result before applying its own check. Here is the correct pattern from the WordPress REST API Handbook:

add_filter( 'rest_authentication_errors', function( $result ) {
 // If a previous authentication check was applied,
 // pass that result along without modification.
 if ( true === $result || is_wp_error( $result ) ) {
 return $result;
 }

 // No authentication has been performed yet.
 // Return an error if user is not logged in.
 if ( ! is_user_logged_in() ) {
 return new WP_Error(
 'rest_not_logged_in',
 __( 'You are not currently logged in.' ),
 array( 'status' => 401 )
 );
 }

 return $result;
} );

The key detail is the first if check. If Application Passwords already authenticated the user, $result will be true and the filter returns early. Without that guard, the is_user_logged_in() check fails for Application Password requests because they authenticate via Basic Auth headers, not cookies, and is_user_logged_in() returns false. The filter then returns a WP_Error and the request fails.

If you cannot modify the filter because it is in a plugin you do not control, you can override it with a higher-priority callback in a must-use plugin that respects the existing authentication result:

// Remove the restrictive filter
remove_filter( 'rest_authentication_errors', 'problematic_callback_function_name' );

// Add a corrected version that respects application password auth
add_filter( 'rest_authentication_errors', function( $result ) {
 if ( true === $result || is_wp_error( $result ) ) {
 return $result;
 }
 if ( ! is_user_logged_in() ) {
 return new WP_Error(
 'rest_not_logged_in',
 'You are not currently logged in.',
 array( 'status' => 401 )
 );
 }
 return $result;
} );

You need to know the exact callback function name to remove it. Search the plugin’s source code for add_filter( 'rest_authentication_errors' to find the function name registered.

Cause 3: Permalink and Rewrite Problems

The WordPress REST API requires pretty permalinks to be enabled. If your permalink structure is set to “Plain” (?p=123), REST routes return 404 because the rewrite rules that map /wp-json/ to index.php?rest_route=/ are not generated. This is one of the most overlooked causes because the site itself works fine in the browser, and the problem only surfaces when you hit the API.

Symptom: curl -i https://example.com/wp-json/ returns 404. The homepage and other pages load normally. The REST API root is simply not found.

How to confirm: Go to Settings > Permalinks in wp-admin. If the permalink structure is set to “Plain,” that is your cause. Even if it is set to a pretty structure, the rewrite rules may be stale or missing. Check whether the REST rewrite rules exist in the database:

wp rewrite list | grep wp-json

If you see rules like ^wp-json/?$ mapping to index.php?rest_route=/, the rules exist. If the output is empty, the rules are missing and need to be flushed.

The fix for plain permalinks: Go to Settings > Permalinks and select any structure other than “Plain” (Post name is the recommended default). Click Save Changes. This regenerates the rewrite rules, including the REST API routes.

The fix for stale rewrite rules: If pretty permalinks are already set but REST still 404s, the rewrite rules may be stale. Flush them by visiting Settings > Permalinks and clicking Save Changes, or from the command line:

wp rewrite flush

Do not use wp rewrite flush --hard unless you know what you are doing. The --hard flag rewrites the .htaccess file directly, which can overwrite custom rules. The standard flush regenerates the rewrite rules in the database, which is what you need.

When the .htaccess file is missing or not writable

If WordPress cannot write to the .htaccess file (due to file permissions), saving permalinks will not update the rewrite rules. Check that .htaccess exists in your site root and contains the standard WordPress rewrite block:

# BEGIN WordPress
RewriteEngine On
RewriteBase /
RewriteRule ^index.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
# END WordPress

If the file is missing, create it and add the rules above. Ensure the file is writable by the web server (chmod 644 is standard). Then flush permalinks again.

Apache AllowOverride None

If your Apache virtual host configuration has AllowOverride None, Apache ignores .htaccess entirely. Permalinks and REST rewrites fail even though the .htaccess file is correct. Check your Apache configuration:

sudo apache2ctl -S | grep -i allowoverride

If you see AllowOverride None for your site’s directory, change it to AllowOverride All in your virtual host config and restart Apache:


 AllowOverride All
sudo systemctl restart apache2

Cause 4: Server Rules Blocking /wp-json/

PHP server-side code displayed in a dark IDE, representing Nginx and Apache server configuration for WordPress

Even when WordPress has the correct rewrite rules, the web server itself can block or misroute requests to /wp-json/. This happens at the Nginx or Apache configuration level, before WordPress processes anything. The symptom is distinctive: curl to /wp-json/ returns 404 or 403, but the rest of the site works fine.

Nginx missing the try_files fallback

Nginx does not use .htaccess files. WordPress rewrite rules are stored in the database, but Nginx needs an explicit try_files directive to pass non-existent paths like /wp-json/ to index.php. If your Nginx server block lacks this directive, every request to /wp-json/ returns 404 because Nginx looks for a physical file at that path, finds nothing, and returns 404 without ever forwarding the request to PHP.

Symptom: curl -i https://example.com/wp-json/ returns 404. The homepage and regular pages work fine.

How to confirm: Check your Nginx server block configuration:

sudo nginx -T 2>/dev/null | grep -A5 'location /'

If you see try_files $uri $uri/ =404; without the index.php fallback, that is your cause. The =404 tells Nginx to return 404 for any path that does not match a physical file, which includes /wp-json/.

The fix: Ensure your Nginx configuration includes the proper try_files fallback:

location / {
 try_files $uri $uri/ /index.php?$args;
}

If you want an explicit location block for wp-json, you can add:

location ~ ^/wp-json/ {
 try_files $uri $uri/ /index.php?$args;
}

After making changes, test the Nginx configuration and reload:

sudo nginx -t
sudo systemctl reload nginx

Nginx or Apache explicitly blocking /wp-json/

Some server configurations explicitly block access to /wp-json/. This can be an intentional security hardening measure or an accidental side effect of a broader deny rule. On Nginx, it looks like this:

location ~ ^/wp-json/ {
 deny all;
 return 403;
}

On Apache, it might be in .htaccess or the virtual host config:


 Require all denied

How to confirm: If curl returns 403 specifically on /wp-json/ but other paths work, check your server configuration for explicit deny rules. Search for “wp-json” in your Nginx or Apache config files.

The fix: Remove or comment out the deny rule. If the rule was added intentionally for security, replace it with an allowlist that permits specific IPs or authenticated requests rather than blocking the entire endpoint.

CORS issues: REST API works in curl but fails in browser

If your curl requests succeed but browser-based requests fail with a CORS error in the console, the REST API is working fine. The browser is blocking the response. Cross-Origin Resource Sharing (CORS) is a browser security mechanism that prevents scripts on one origin from accessing resources on a different origin unless the server explicitly allows it.

WordPress core sends permissive CORS headers by default. The rest_send_cors_headers() function reflects the incoming Origin header back as Access-Control-Allow-Origin, along with Access-Control-Allow-Credentials: true. This is an intentional design decision documented in the WordPress REST API FAQ: WordPress uses nonces for CSRF protection instead of CORS, so the REST API does not verify the Origin header.

If CORS errors appear, something has removed or overridden the default CORS headers. A security plugin or a custom filter may have unhooked rest_send_cors_headers from the rest_pre_serve_request filter. Search your codebase:

grep -r "rest_send_cors_headers" wp-content/

The fix: If the default CORS function has been removed, re-add it:

add_filter( 'rest_pre_serve_request', 'rest_send_cors_headers' );

If you need stricter CORS headers that only allow specific origins, replace the default function with a custom one:

remove_filter( 'rest_pre_serve_request', 'rest_send_cors_headers' );

add_filter( 'rest_pre_serve_request', function( $value ) {
 $allowed_origins = array( 'https://app.yoursite.com', 'https://dashboard.yoursite.com' );
 $origin = get_http_origin();

 if ( $origin && in_array( $origin, $allowed_origins, true ) ) {
 header( 'Access-Control-Allow-Origin: ' . $origin );
 header( 'Access-Control-Allow-Methods: OPTIONS, GET, POST, PUT, PATCH, DELETE' );
 header( 'Access-Control-Allow-Credentials: true' );
 header( 'Access-Control-Allow-Headers: Authorization, Content-Type' );
 header( 'Vary: Origin', false );
 }

 return $value;
} );

Note: CORS only affects browser-based requests. Server-to-server requests via curl, Python, PHP, or any backend language are not subject to CORS restrictions. If your integration is server-side and you see “CORS” in the error, the issue is likely something else.

Cause 5: Host-Level WAF Blocking REST API Requests

Blue-lit data center server room with network equipment labeled NETWORK-2, representing hosting-level WAF infrastructure

When you have ruled out plugins, filters, permalinks, and server configuration, the remaining suspect is the hosting layer itself. Managed WordPress hosts, CDN providers, and server-level firewalls can all intercept requests to /wp-json/ before they reach WordPress. This is the hardest cause to diagnose because you do not control the infrastructure and the blocks are often invisible.

Cloudflare WAF and Bot Fight Mode

Cloudflare is the most common host-level WAF that blocks REST API requests. Bot Fight Mode, Super Bot Fight Mode, and managed WAF rules can all flag automated API requests as bot traffic and return 403. The signature symptom: requests work when Cloudflare is in Development Mode or when the DNS record is set to “DNS only” (gray cloud), but fail when proxied (orange cloud).

Symptom: HTTP 403 with a Cloudflare error page (Error 1020) or a generic blocked response. The response body may contain “Access denied” or reference Cloudflare’s firewall. Check the response headers for cf-ray or server: cloudflare to confirm the request passed through Cloudflare.

How to confirm: Temporarily set the DNS record for your domain to “DNS only” (gray cloud in the Cloudflare dashboard) and retest. If the request succeeds without Cloudflare proxying, the WAF or bot protection is the cause. Alternatively, check Security > Events in the Cloudflare dashboard for blocked requests matching your API calls.

The fix: Create a WAF exception or custom rule that skips firewall checks for /wp-json/ requests. In Cloudflare, create a custom firewall rule:

Go to Security > WAF > Custom Rules and create a rule with this expression:

(http.request.uri.path contains "/wp-json/")

Set the action to “Skip” for all remaining rules. This bypasses Bot Fight Mode, managed WAF rules, and rate limiting for any request to the REST API. If you need tighter control, add an IP condition so only your automation tool’s IP is allowlisted:

(http.request.uri.path contains "/wp-json/") and (ip.src eq 203.0.113.50)

Also check if Bot Fight Mode is enabled under Security > Bots. If your API calls come from a server with a static IP, disabling Bot Fight Mode is safe. If you cannot disable it, the WAF exception rule above will skip it for /wp-json/ paths.

ModSecurity and server-level firewalls

ModSecurity is a server-level WAF that many hosting providers enable by default. It runs the OWASP Core Rule Set, which can flag REST API requests containing JSON payloads, SQL-like strings, or HTML content as suspicious. The block happens at the Apache or Nginx module level, before WordPress processes the request.

Symptom: HTTP 403 with a response body that mentions ModSecurity, or a response header containing Mod_Security or NS-BLOCK. The block may be intermittent: simple GET requests work, but POST requests with larger payloads fail.

How to confirm: Check your server’s error log for ModSecurity entries:

sudo tail -100 /var/log/apache2/error.log | grep -i modsec

Or on cPanel hosts, check the ModSecurity Tools section in cPanel. If you see entries matching your API requests, ModSecurity is blocking them.

The fix: If you have server access, disable the specific ModSecurity rule that is triggering the block. Find the rule ID in the error log (it looks like [id "949110"]) and disable it in your ModSecurity configuration:


 SecRuleRemoveById 949110

If you do not have server access, contact your hosting provider and ask them to whitelist the ModSecurity rule that is blocking your REST API requests. Provide the rule ID from the error log. Most hosts will add the exception on request.

Managed WordPress hosts stripping headers or blocking API access

Some managed WordPress hosts disable or restrict the REST API at the platform level. They may strip the Authorization header, block requests to /wp-json/ through their own firewall, or disable Application Passwords in their WordPress configuration. This is beyond your control via .htaccess or Nginx config because the host intercepts the request before it reaches your site’s configuration.

Symptom: You have tried every fix in this guide and the REST API still fails. The Authorization header is missing even after adding the .htaccess or Nginx fixes. The application passwords section is missing despite HTTPS being enabled.

How to confirm: Contact your hosting provider’s support team and ask specifically: “Does your platform pass the Authorization header to PHP, and are there any restrictions on REST API access at the hosting level?” If they confirm they strip the header or block /wp-json/, you have found the cause.

The fix: Request that the host enable Authorization header forwarding and remove any REST API restrictions for your account. Most hosts will do this on request. If they will not, switch to a host that supports standard WordPress REST API functionality without platform-level interference.

Comparison: Symptoms and Solutions at a Glance

CauseHTTP StatusKey SymptomConfirmation MethodFix
Security plugin401 or 403Fails after installing Wordfence, Solid Security, etc.Deactivate plugins one by one, retestWhitelist IP or allowlist REST endpoints in plugin settings
rest_authentication_errors filter401All requests fail with rest_not_logged_in, including public GETgrep -r “rest_authentication_errors” wp-content/Add early return for authenticated requests in filter callback
Permalink or rewrite issue404/wp-json/ returns 404, rest of site worksCheck Settings > Permalinks, run wp rewrite listSet pretty permalinks, flush rewrite rules
Server rule blocking /wp-json/404 or 403404 on Nginx without try_files, 403 with deny ruleCheck nginx -T for try_files and deny rulesAdd try_files fallback, remove deny rules
CORS (browser only)200 in curl, blocked in browserConsole shows CORS policy errorCompare curl vs browser response headersRe-add or customize rest_send_cors_headers filter
Host-level WAF403 or 503Cloudflare Error 1020, works with proxy disabledCheck Cloudflare Security Events, test with gray cloudCreate WAF skip rule for /wp-json/, whitelist IP
ModSecurity403Intermittent, POST fails but GET worksCheck error log for ModSecurity rule IDsDisable specific rule by ID or contact host

Diagnostic Checklist: Fix It in Order

Work through these steps in sequence. Most failures are resolved by step 3. If you reach the end and the REST API still fails, the cause is at the hosting layer and you need to contact your host.

  • Run curl -i https://example.com/wp-json/. If 404, go to step 5 (permalinks). If 403, go to step 3. If 200, the API is reachable.
  • Run curl -i -u "user:app_password" /wp-json/wp/v2/users/me. If 401, check whether the Authorization header is being stripped (see the application password guide). If 200, auth works.
  • Deactivate all security plugins and retest. If the request succeeds, reactivate one by one to find the culprit. Configure the offending plugin to allowlist your IP or REST endpoints.
  • Search for rest_authentication_errors filters in your codebase. If found, verify the callback passes through existing auth results before checking is_user_logged_in().
  • Check Settings > Permalinks. If set to “Plain,” switch to “Post name” and save. If already pretty, flush rewrite rules with wp rewrite flush.
  • Check Nginx or Apache config for missing try_files fallback or explicit deny rules on /wp-json/. Fix the config and reload the server.
  • If curl works but browser requests fail, check for CORS issues. Verify rest_send_cors_headers is hooked.
  • If all else fails, check for host-level WAF blocks. Test with Cloudflare proxy disabled. Check ModSecurity logs. Contact your host.

If you are automating WordPress content publishing and hitting these issues repeatedly across multiple sites, ClearPost handles the full authentication and publishing pipeline internally. The plugin authenticates using WordPress’s native application password system and manages post creation, featured images, SEO metadata, and taxonomy assignment. You approve every post before it goes live. No manual API debugging, no curl scripts to maintain, no server configuration to troubleshoot.

Try ClearPost free for 7 days. AI does the heavy lifting, you approve every post before it goes live. No long onboarding, no agency overhead, cancel anytime.

Frequently Asked Questions

How can I tell if the WordPress REST API is disabled or if authentication is failing?

Run curl -i https://example.com/wp-json/ without credentials. If it returns HTTP 200 with a JSON response listing namespaces, the REST API is enabled and routing correctly. If authenticated requests fail with 401 but unauthenticated GET requests work, the API is not disabled. Your credentials are not reaching PHP, usually because the server strips the Authorization header. If /wp-json/ itself returns 404, the API is not routing at all due to permalink or server configuration issues.

Can security plugins block the WordPress REST API?

Yes. Wordfence, Solid Security, Really Simple Security, and All In One WP Security can all block REST API requests. Wordfence’s WAF can flag POST requests with JSON payloads as suspicious. Solid Security can restrict REST API access to authenticated users only. Really Simple Security’s user enumeration protection can block /wp-json/wp/v2/users endpoints. Deactivate all security plugins and retest to confirm, then configure the offending plugin to allowlist your IP or REST endpoints.

What does the rest_authentication_errors filter do and how can it break the REST API?

The rest_authentication_errors filter is the official WordPress mechanism for requiring authentication on REST API requests. If a callback returns a WP_Error, all unauthenticated requests fail. The common bug: callbacks that check is_user_logged_in() without first checking if authentication already succeeded will reject application password requests, because application passwords authenticate via Basic Auth headers, not cookies, and is_user_logged_in() returns false for them. The fix is to return early if $result is true or is a WP_Error.

Why does /wp-json/ return 404 on my WordPress site?

The most common cause is that pretty permalinks are not enabled. Go to Settings > Permalinks, select any structure other than Plain, and save. This generates the rewrite rules that map /wp-json/ to index.php. If permalinks are already set to a pretty structure, the rewrite rules may be stale. Flush them by re-saving permalinks or running wp rewrite flush. On Nginx, also verify your server block includes try_files $uri $uri/ /index.php?$args; so non-existent paths like /wp-json/ are forwarded to PHP.

How do I fix Cloudflare blocking my WordPress REST API requests?

Create a WAF custom rule in Cloudflare with the expression (http.request.uri.path contains “/wp-json/”) and set the action to Skip for all remaining rules. This bypasses Bot Fight Mode, managed WAF rules, and rate limiting for REST API requests. You can add an IP condition for tighter control. Also check Security > Events in the Cloudflare dashboard to confirm which rule is blocking your requests and find the exact rule ID.