WordPress REST API Authentication and Create Posts
Reading public posts needs no login, but to create, change or delete anything, or to see private data, the API must know who you are. WordPress has two main ways to do it.
| Method | Used by | How it works |
|---|---|---|
| Cookie and nonce | Scripts running inside the logged in dashboard or theme (like the block editor) | The login cookie plus the X-WP-Nonce header |
| Application passwords | Outside apps, scripts and other servers | A special password per app, sent with Basic authentication |
Without a login
$ curl -s -X POST "http://example.com/wp-json/wp/v2/posts" -d "title=Hello" | jq -c '{code, message, status: .data.status}'
{"code":"rest_cannot_create","message":"Sorry, you are not allowed to create posts as this user.","status":401}
The server answered 401. Nobody is logged in, so the request is refused.
Application passwords
Application passwords are built in since WordPress 5.6. Each user can create many of them, in Users > Profile > Application Passwords, give them a name and delete them at any time. They only work for the REST API (and XML-RPC), and not for the normal login. WordPress shows the password only once, in this style:
abcd EFgh 1234 ijkl 5678 mnop
You can also create one from the command line: wp user application-password create admin "My App" --porcelain. Then you send the username and this password with Basic authentication (curl -u). On a live site your site must use HTTPS for this to work (WordPress allows it on local and development sites only).
Create a post
Replace APP_PASSWORD with the password of your app:
$ curl -s -u "admin:APP_PASSWORD" -X POST "http://example.com/wp-json/wp/v2/posts" -d "title=Created with the API" -d "content=Hello from curl" -d "status=publish" | jq -c '{id, status, slug, author, type}'
{"id":5,"status":"publish","slug":"created-with-the-api","author":1,"type":"post"}
The answer has the code 201 and the new post (we show only a few fields). You can also send a JSON body, which is the usual way in applications:
$ curl -s -u "admin:APP_PASSWORD" -X POST "http://example.com/wp-json/wp/v2/posts" -H "Content-Type: application/json" -d '{"title":"A draft from JSON","status":"draft"}' | jq -c '{id, status}'
{"id":6,"status":"draft"}
Update a post
Send the new values to the route of that post (POST, PUT or PATCH all work):
$ curl -s -u "admin:APP_PASSWORD" -X POST "http://example.com/wp-json/wp/v2/posts/6" -H "Content-Type: application/json" -d '{"title":"Created with the API (edited)"}' | jq -c '{id, title: .title.rendered}'
{"id":6,"title":"Created with the API (edited)"}
Delete a post
A normal delete moves the post to the trash. With force=true it is removed forever:
$ curl -s -u "admin:APP_PASSWORD" -X DELETE "http://example.com/wp-json/wp/v2/posts/6" | jq -c '{status: .status}'
{"status":"trash"}
$ curl -s -u "admin:APP_PASSWORD" -X DELETE "http://example.com/wp-json/wp/v2/posts/6?force=true" | jq -c '{deleted, previous: .previous.status}'
{"deleted":true,"previous":"trash"}
Wrong password
If the password is wrong, WordPress does not say “wrong password”. It just treats you as a guest, so you get the same 401 error as before:
$ curl -s -u "admin:wrong password" -X POST "http://example.com/wp-json/wp/v2/posts" -d "title=Hello" | jq -c '{code, status: .data.status}'
{"code":"rest_cannot_create","status":401}
Cookie authentication with a nonce
When your JavaScript runs on a page of the logged in user, the browser already sends the cookie. WordPress also needs a nonce in the X-WP-Nonce header, to stop other sites from using your cookie. We log in like a browser and use the nonce:
$ NONCE=$(curl -s -b cookies.txt "http://example.com/wp-admin/admin-ajax.php?action=rest-nonce"); curl -s -b cookies.txt -H "X-WP-Nonce: $NONCE" -X POST "http://example.com/wp-json/wp/v2/posts" -H "Content-Type: application/json" -d '{"title":"Made with cookie and nonce","status":"draft"}' | jq -c '{id, status}'
{"id":8,"status":"draft"}
$ curl -s -b cookies.txt -X POST "http://example.com/wp-json/wp/v2/posts" -H "Content-Type: application/json" -d '{"title":"No nonce","status":"draft"}' | jq -c '{code, status: .data.status}'
{"code":"rest_cannot_create","status":401}
The second request has the cookie but no nonce, so WordPress treats the user as logged out. In your own theme or plugin script, you give the nonce to JavaScript with wp_create_nonce( 'wp_rest' ).
Which one to use?
- Your own theme or plugin JavaScript: cookie and nonce.
- A mobile app, a script or another server: application passwords over HTTPS.
- Never put the admin username and password in a script. An application password is safer, because you can delete it at any time.
| Exercises & Assignments |
|---|
| No Content Found. |
| Interview Questions & Answers |
|---|
| No Content Found. |