DEV3 Technologies home

DEV3 Developer Forum

How do I add a secure custom REST API endpoint in WordPress?

WordPress helpSolved0 views
dev3 ·

I registered a REST route that saves data for logged-in users, but I get 403 rest_cookie_invalid_nonce from JavaScript and I am not sure my permission handling is safe.

#wordpress#rest-api#security

0

1 reply

dev3 · Accepted answer

Register on rest_api_init, always provide a permission_callback, validate and sanitize arguments, and send a nonce from JavaScript.

add_action( 'rest_api_init', function () {
    register_rest_route( 'acme/v1', '/notes', array(
        'methods'             => 'POST',
        'callback'            => 'acme_save_note',
        'permission_callback' => function () {
            return current_user_can( 'edit_posts' );
        },
        'args' => array(
            'text' => array(
                'required'          => true,
                'type'              => 'string',
                'sanitize_callback' => 'sanitize_textarea_field',
                'validate_callback' => function ( $v ) { return strlen( $v ) <= 500; },
            ),
        ),
    ) );
} );

function acme_save_note( WP_REST_Request $req ) {
    $id = wp_insert_post( array(
        'post_type'    => 'note',
        'post_status'  => 'private',
        'post_title'   => wp_trim_words( $req['text'], 6 ),
        'post_content' => $req['text'],
    ) );
    return rest_ensure_response( array( 'id' => $id ) );
}

Client side, WordPress cookie authentication needs the REST nonce in a header:

fetch('/wp-json/acme/v1/notes', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': ACME.restNonce },
  body: JSON.stringify({ text: 'Hello' })
});
wp_localize_script( 'acme-app', 'ACME', array( 'restNonce' => wp_create_nonce( 'wp_rest' ) ) );

Why you saw rest_cookie_invalid_nonce: the nonce was missing, expired, or created for a different action. It must be a wp_rest nonce. Without it WordPress treats the request as unauthenticated.

Pitfalls

  • Omitting permission_callback triggers a notice (since WP 5.5), and __return_true makes the route public.
  • Check capabilities, not just "logged in".
  • Use external API clients with Application Passwords (Basic auth over HTTPS) instead of cookies.

A tested pattern is in the DEV3 snippet library.

0

Your reply

Every post is reviewed by DEV3 before it appears. Usually reviewed within 24 hours.

Markdown supported. Use ``` for code blocks. Images are not allowed.