Securing Headless WordPress with Application Passwords
Stop using master passwords for your headless WordPress integrations. Learn how to implement Application Passwords for secure, revocable, and programmable REST API access.
05 Aug 2026, 13:36 UTC

The Problem: Avoiding the 'Master Password' Trap
When building a decoupled or headless WordPress site, you often need a way for your frontend—or a CI/CD pipeline—to programmatically create posts, update content, or manage media. The instinctive reaction is to use the administrator's main password in a Basic Auth header. This is a critical security risk: if that token is leaked, your entire site is compromised, and rotating the password breaks every other integration and user login.
The solution is Application Passwords (introduced in WordPress 5.6). Instead of using a primary account password, you generate unique, revocable tokens for specific integrations. If your mobile app's token is leaked, you revoke that single token without affecting your admin login or your CI/CD pipeline.
How Application Passwords Work
Application Passwords provide a stateless authentication method for the WordPress REST API. Unlike cookie-based authentication, which relies on browser sessions and CSRF tokens, Application Passwords use a 24-character string that identifies the user and authorizes the request.
Crucially, these passwords are stored as bcrypt hashes in the wp_usermeta table. This means that once a password is generated and shown to you, it cannot be retrieved from the database. If you lose it, you must delete the old one and create a new one.
Permission Scoping and Limitations
A common misconception is that Application Passwords allow you to limit a token to "read-only" or "posts-only" access. They do not. An Application Password inherits the full capabilities of the user account that created it. If an Administrator creates a token, that token has full administrative power over the REST API.
To implement granular control, you must use the permission_callback within your custom REST routes. This allows you to check for specific capabilities (like edit_posts) regardless of the authentication method used.
Implementation Example: Server-to-Server Update
Because Application Passwords bypass CSRF protection and are sent in the Authorization header, they should never be embedded in client-side JavaScript. They are intended for server-to-server communication or secure backend proxies.
Below is a practical example of using curl to update a post's content. This command should be run from a secure server or a local terminal with HTTPS enabled.
# Replace placeholders with your actual data
# -u 'username:application_password' handles the Basic Auth encoding
# -X POST is used for updates (or PATCH)
# --data-raw sends the JSON payload
curl -u 'editor_user:abcd 1234 efgh 5678 ijkl 9012' \\
-X POST https://your-site.com/wp-json/wp/v2/posts/123 \\
-H "Content-Type: application/json" \\
--data-raw '{"content": "This content was updated via the REST API using an Application Password."}'
Expected Result: A 200 OK response containing the updated JSON object of the post. If the password is incorrect or revoked, you will receive a 401 Unauthorized.
Automating Rotation with WP-CLI
For production environments, manually generating passwords in the dashboard is inefficient. You can automate credential rotation in your CI/CD pipeline using WP-CLI. Run these commands on the server where WordPress is installed with the appropriate shell permissions.
- Create a token:
wp user application-password create username 'ci-deploy' --porcelain. The--porcelainflag ensures only the plaintext password is output, making it easy to pipe into a secret manager. - List tokens:
wp user application-password list username. - Revoke a token:
wp user application-password delete username [ID].
Engineering Trade-offs and Risks
| Consideration | Risk/Limitation | Mitigation |
|---|---|---|
| Rate Limiting | WordPress core does not rate-limit the REST API, leaving you open to brute-force or DoS attacks. | Implement rate_limit_zone in Nginx or use a CDN like Cloudflare to throttle requests. |
| CORS | Browser-based requests from different domains will be blocked by default. | Configure Access-Control-Allow-Origin headers at the server level or via the rest_api_init hook. |
| Privilege Escalation | Users with edit_users capability can create tokens for other users. |
Restrict the edit_users capability in multi-author environments. |
Verification Checklist
To ensure your implementation is secure and functional, perform these three checks:
- Revocation Test: Delete the Application Password in the WordPress dashboard. Immediately attempt the
curlrequest again; it must return a401 Unauthorized. - HTTPS Enforcement: Attempt a request over HTTP. Ensure your server redirects to HTTPS or rejects the request, as Application Passwords are sent in plaintext within the Basic Auth header.
- CORS Validation: Run
curl -H 'Origin: https://your-frontend.com' -I https://your-site.com/wp-json/wp/v2/posts. Verify that theAccess-Control-Allow-Originheader is present and correctly configured.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.