WordPress

WordPress REST API Explained: Uses, Authentication and Security

What is the WordPress REST API used for?

A support ticket that arrives late at night often starts with the same worry: “The REST API is visible. Has the site been hacked?” Most of the time, no. WordPress exposing public posts as JSON is expected behavior. The useful questions are which data is exposed and whether endpoints that change data are protected.

I use the WordPress REST API as a way to access site data over HTTP without opening the WordPress dashboard. A client can read a post, create content, update a product, or send information to another application.

Let me put it this way: the dashboard is the familiar interface for working with WordPress. The REST API is another controlled door into selected resources. The door exists, but it should not open every room for every visitor.

A standard REST API URL usually looks like this:

https://example.com/wp-json/wp/v2/posts

This endpoint returns the site’s posts. /wp/v2/pages represents pages, /wp/v2/media represents media files, and, when WooCommerce is installed, /wc/v3/products represents products when the request is authenticated correctly.

I once had a customer see this URL for the first time and assume the site had been compromised. Listing public posts as JSON is normal on most WordPress installations. The part that deserves investigation is an unauthorized response containing private data such as customer addresses, internal notes, or product costs.

How requests and responses work

The REST API works with resources. A post, page, user, or media file is a resource, while the HTTP method describes the operation. WordPress core commonly uses POST for both creating and updating resources, so the exact endpoint documentation matters.

HTTP methodTypical useAuthentication
GETRead and list dataUsually not required for public content
POSTCreate or update a resourceRequired for protected resources
DELETEDelete a resourceRequired

For example, this request returns the latest posts:

curl -s 'https://example.com/wp-json/wp/v2/posts?per_page=3&_fields=id,date,link,title'

per_page=3 asks for three records. _fields leaves unnecessary fields out of the response. Smaller responses help integrations that run frequently, although they do not remove the database work required to find the records.

WordPress returns the total record count in HTTP headers. X-WP-Total shows the total number of records, while X-WP-TotalPages shows the number of result pages. When I move a large content archive, I do not inspect only the JSON body; I check these headers too.

To request one post by its ID:

curl -s 'https://example.com/wp-json/wp/v2/posts/123'

123 is the post ID. If the ID does not exist, or the resource is not published and the request is unauthenticated, you may receive a 404-style REST error. A failed REST API URL does not automatically mean the hosting service is down. Permalink rules, a security plugin, or a web server rule can affect the request.

What people build with the API

The WordPress REST API lets you separate the system that produces content from the interface that displays it. This is often called headless WordPress. Your whole site does not have to be headless because one integration uses the API.

  • Providing posts, categories, and media information to a mobile application.
  • Displaying WordPress content in a React, Vue, or another JavaScript interface.
  • Synchronizing content and product data with an external CRM, ERP, or inventory system.
  • Automating repetitive tasks for content editors.
  • Collecting announcements from several WordPress sites in one shared interface.
  • Listing or updating WooCommerce products in an authorized internal application.

You can also feed content into SEO and measurement systems. Fetching data through an API does not mean Google will automatically use that data in search results. The integration and the search engine’s behavior are separate matters. For another way to verify a measurement setup, see GA4 Setup Guide: Track Your Website Data Step by Step.

WooCommerce product and order endpoints need extra care. A product’s public name and price do not belong to the same security class as a customer’s address. I define the fields an integration needs instead of returning a complete object by default.

Reading data with the WordPress REST API

Filtering, searching, and pagination

Query parameters let you narrow a response. This request asks for the second page of posts containing a search term and assigned to a particular category:

curl -s 'https://example.com/wp-json/wp/v2/posts?search=linux&categories=7&per_page=10&page=2'

If page is higher than the available page count, the API can return an error. Automation needs to expect that response rather than treating every non-200 result as a server failure.

Use _fields to select only what you need:

curl -s 'https://example.com/wp-json/wp/v2/posts?per_page=5&_fields=id,slug,title.rendered,excerpt.rendered'

Fields containing HTML appear as title.rendered or excerpt.rendered. If another interface displays them, do not treat the raw response as trusted markup. Escape or sanitize it in the layer that renders it.

Content status and the context parameter

Unauthenticated requests generally use the view context. Authorized users can request the edit context, which may return additional fields. Drafts, private fields, and editing information can appear there.

Using context=edit does not grant permission by itself. WordPress still checks whether the requesting user can edit the resource. Hiding private fields only in the client is a mistake I see too often.

Authentication methods

Cookie and nonce authentication for logged-in sessions

A user logged into the WordPress dashboard can send REST API requests from JavaScript running in the browser. WordPress uses the session cookie together with a wp_rest nonce. The nonce helps confirm that the request belongs to the expected WordPress session.

This works well for screens inside the site. It is not the right way to build a long-running integration on an external server. I once found a test integration carrying a copied browser cookie long after the original debugging session had ended. We revoked the session immediately and replaced it with a dedicated credential.

Application Passwords

WordPress includes Application Passwords for connecting an application to a user account. Send the credential over HTTPS using Basic Authentication:

curl --user 'api-user:application-password' \
  -H 'Content-Type: application/json' \
  'https://example.com/wp-json/wp/v2/users/me'

The response shows which user authenticated the request. Do not share an Application Password like a normal account password, commit it to a repository, or attach it to an administrator account when a less privileged user will do.

A request to create a post might look like this:

curl --user 'api-user:application-password' \
  -X POST 'https://example.com/wp-json/wp/v2/posts' \
  -H 'Content-Type: application/json' \
  -d '{"title":"Draft created through the API","content":"Content goes here","status":"draft"}'

This creates a draft post. Before running it on a live site, check the target site, user, and status value. A POST sent to the wrong endpoint is not a reversible preview.

When should you consider JWT or OAuth?

JWT or OAuth may make sense for a custom mobile application or a separate service architecture. They are not identical authentication features provided by WordPress core on every installation. They usually require a plugin, reverse proxy, or separate identity service.

A popular plugin is not automatically safe when its settings are left at their defaults. Review token lifetime, renewal, revocation, TLS, and whether tokens can appear in error logs. For a small integration, I usually start with built-in Application Passwords and a user with the minimum required permissions. I do not add JWT before there is a real need for it.

Writing a custom REST endpoint safely

A plugin or theme can register a custom endpoint:

register_rest_route( 'store/v1', '/stock/(?P<sku>[A-Za-z0-9_-]+)', array(
    'methods'             => WP_REST_Server::READABLE,
    'callback'            => 'store_get_stock',
    'permission_callback' => 'store_stock_permission',
) );

The namespace is versioned and the SKU has a defined pattern. The security decision belongs in permission_callback. Leaving it empty, or copying an old __return_true example from a tutorial, is risky.

You can connect the permission check to a user capability:

function store_stock_permission( WP_REST_Request $request ) {
    return current_user_can( 'manage_woocommerce' );
}

What did we do? The callback allows only users who can manage WooCommerce. Choose a capability that matches the site’s role structure. Using manage_options for every operation gives more access than many integrations need.

Input validation and output sanitization

Data received through the REST API is untrusted. Check its type, allowed values, length, and business context. If a quantity should be an integer, validate it instead of silently accepting an arbitrary string. Use the appropriate WordPress sanitization functions for text fields.

Returning every database field unchanged is another mistake. If an email address, physical address, internal note, customer ID, or private meta field is not needed, leave it out. Be especially careful with fields added through register_rest_field. Exposing a field is easy; discovering later that it was public is expensive.

Nonces, CORS, and HTTPS

A nonce is not a replacement for authentication. CORS is not an authorization system either; it controls which browser origins may read a response. You still need to authenticate the request and check what the user is allowed to do.

Credentials, Application Passwords, and content update requests must travel over HTTPS. Do not rely on an HTTP redirect and send a password to an HTTP URL first. The client should use the correct HTTPS address directly.

Should you disable or protect the REST API?

A response containing public posts is not, by itself, a security vulnerability. Disabling the entire API can break the block editor and modern plugins. I prefer to identify the endpoint that creates the risk.

Restricting user enumeration, protecting sensitive custom endpoints, and removing unnecessary plugins is often healthier than shutting down the whole API. Test that a security plugin’s broad rule does not break the mobile application or editor connection you need.

The checks described in WordPress Site Health: How to Read and Fix the Report are useful here too. When a REST API error appears, do not blame the security plugin immediately. Check the PHP version, permalink rules, the REST test, loopback requests, and server logs together.

If custom endpoints perform administrative actions, leave an audit trail. You should later be able to answer who changed which record, when, and through which client. Logging and Audit Trail Architecture for GDPR/KVKK‑Compliant Admin Actions covers the personal-data side of these records separately.

Performance and operations

If a client fetches thousands of posts on every page load, the API may be working correctly while the design is still wrong. Use pagination, _fields, suitable caching, and ETag or Last-Modified headers for data that does not change often.

Keep authorized and unauthorized responses separate in the cache layer. A response requested with context=edit, or one containing user-specific data, must not land in a public cache. Check how your CDN handles requests carrying the Authorization header.

Rate limiting is not only for attack traffic. A badly written synchronization job can send hundreds of requests per second to one endpoint and consume PHP workers. On the hosting side, I check access logs, application logs, and CPU and RAM monitoring. When a ticket says “the API is slow,” I first measure request volume, response size, and query parameters. More server capacity comes later.

Do not log passwords or tokens while adding monitoring. For most troubleshooting, the endpoint, HTTP status, and, when appropriate, the user ID are enough. A useful audit trail does not require storing sensitive values.

My pre-launch checklist

Before I put a new WordPress REST API integration into production, I check the following:

  1. Is the endpoint namespace and version clearly defined?
  2. Is the data exposed through GET genuinely public?
  3. Do POST and DELETE requests check user capabilities?
  4. Do failed permission checks return the expected 401 or 403 response?
  5. Are input validation, type checks, and output sanitization in place?
  6. Are passwords, tokens, customer addresses, and unnecessary private fields absent from the response?
  7. Have HTTPS, CORS, rate limiting, and cache behavior been tested?
  8. Will the logs contain enough information to troubleshoot without exposing sensitive data?
  9. Is there a rollback plan and a database backup?

Just as I do not run a WordPress update without first taking a wp db export, I finish staging tests before connecting a live integration. Testing an API call in staging is easy. Reversing an automation that accidentally emailed a real customer or changed a live product is not.

Once, a security plugin caused the block editor’s REST request to return 403. The dashboard opened normally, but saving a post failed silently. I checked the response headers with curl -i and compared them with the web server log. The problem was not WordPress core; it was a broad REST rule in the security plugin. We narrowed the rule instead of disabling the entire API, then repeated tests for post creation, media uploads, and unauthorized requests in staging. I had assumed that a working dashboard meant the API was healthy. It was a useful correction.

Frequently asked questions

Is the WordPress REST API public?

On most installations, some fields from public resources such as posts and pages can be read without authentication. Creating, updating, and deleting content, as well as accessing private data, requires authorization. Plugins can change this behavior on your site.

Should the WordPress REST API be disabled?

Usually, it is better to restrict sensitive endpoints and user information than to disable the entire API. A complete shutdown can make the block editor, mobile applications, or some plugins stop working.

Do you need a plugin to use the REST API?

WordPress core provides built-in endpoints for many resources, including posts, pages, media, and users. A plugin or custom code may be needed for JWT authentication, special workflows, or custom business logic.

Does the REST API slow down a site?

The API does not automatically make a site slow. Pulling too many records, expensive meta queries, and uncontrolled automation can create load. Pagination, _fields, caching, and rate limiting reduce that load in a measurable way.