wppaste
WordPress

wp_create_nonce( string|int $action = -1 ): string

Since
2.0.3, 4.0.0
Source
wp-includes/pluggable.php:2531

Generates a short-lived, one-time-use style token scoped to the current user, their session, and a given action string, for use in forms, URLs, and AJAX requests. The token also depends on wp_get_session_token() and a rotating time window from wp_nonce_tick(), so the same action string produces a different value once the user logs out, switches sessions, or enough time passes. Pair it with wp_verify_nonce() or check_ajax_referer() on the receiving end, since creating a nonce alone does nothing to protect a request.

Creates a cryptographic token tied to a specific action, user, user session, and window of time.

Compatibility

WordPress
since 4.0.0
PHP
7.4–8.6-dev
  • 6.7.7
  • 6.8.8
  • 6.9.7
  • 7.0.4
  • 7.1.0

Present in every tracked release (6.7.7 to 7.1.0), and compiles on PHP 7.4 through 8.6-dev.

Parameters

$actionstring|intoptional
Scalar value to add context to the nonce.Default: -1

Return value

string
The token.

Code examples

Every example is editable and runs in a real WordPress booted in your browser by WordPress Playground. Press Run, then edit the code: clicking away re-runs it. Nothing is sent anywhere until you do.

Create a nonce for a delete-post link

Build the nonce that would be appended to a custom admin action URL for deleting post ID 2.

$post_id = 2;
$action  = 'delete_post_' . $post_id;
$nonce   = wp_create_nonce( $action );

$url = add_query_arg(
	array(
		'action'   => 'my_plugin_delete_post',
		'post'     => $post_id,
		'_wpnonce' => $nonce,
	),
	admin_url( 'admin-post.php' )
);

echo esc_html( 'Nonce: ' . $nonce ) . "\n";
echo esc_html( 'URL: ' . $url );

The action string must be reproduced exactly when the request is verified, so build it from the same post ID rather than a hardcoded string.

Create and immediately verify a nonce to check the round trip

Confirm that a nonce created for one action string fails verification against a different action string on post 3.

$correct_action = 'update_price_3';
$wrong_action   = 'update_price_4';

$nonce = wp_create_nonce( $correct_action );

$result_correct = wp_verify_nonce( $nonce, $correct_action );
$result_wrong   = wp_verify_nonce( $nonce, $wrong_action );

echo esc_html( 'Verifies against matching action: ' . var_export( (bool) $result_correct, true ) ) . "\n";
echo esc_html( 'Verifies against mismatched action: ' . var_export( (bool) $result_wrong, true ) );

wp_verify_nonce() returns 1 or 2 (truthy) on success and false on failure, not a plain boolean.

Common problems and fixes · 4

Why does the nonce I generated fail verification a few minutes later?

wp_nonce_tick() divides time into a rotating window, and the hash that wp_create_nonce() returns changes each time that window rolls over. A nonce from an earlier tick can still pass wp_verify_nonce() for one more window, but not indefinitely. - Generate the nonce right before it's used (page load or AJAX call), not far in advance. - Don't cache a nonce value across long-lived page loads or long user sessions.

Why do logged-out users all get the same nonce for the same action?

When wp_get_current_user() resolves to a guest, the user ID is 0, and wp_create_nonce() runs that 0 through the nonce_user_logged_out filter before hashing. Unless something hooks that filter to add per-visitor entropy, every anonymous visitor with the same session state gets an identical nonce for a given action. - Hook nonce_user_logged_out if you need per-visitor uniqueness for guests. - Otherwise treat guest nonces as a light anti-CSRF check, not a user-identity check.

Why does a nonce that worked in one browser tab fail in another?

The function mixes wp_get_session_token() into the hash, so a nonce is tied to the logged-in session it was created in. Logging out, logging back in, or having WordPress issue a new session token invalidates every nonce created under the old token, even if the action string and user are unchanged.

Why did changing my action string break all my existing nonces?

$action is concatenated directly into the string that wp_hash() hashes, so any change to it, including a typo or a switch from a static string to one built from a variable, produces a completely different token. The generating call and the verifying call must use the exact same value. - Store the action string in one constant or function and reuse it on both ends. - Avoid rebuilding the action string with different formatting in different places.

Alternatives and related functions

wp_verify_nonce
When you need to check a nonce that was previously created with wp_create_nonce() rather than generate a new one.
wp_nonce_field
When you need to output a hidden form field containing the nonce plus a referer field, instead of handling the raw string yourself.
wp_nonce_url
When you need to append a nonce to a URL's query string rather than embed it in a form.
check_ajax_referer
When you're handling an AJAX request and want nonce creation and verification plus a die() on failure handled in one call.

Performance profile

How much work a call to wp_create_nonce() does, and what it touches: the algorithmic scaling, the Zend instruction count per call across PHP versions, the hooks it hands control to, and the core code that calls it. Measured from the compiled opcodes, not a stopwatch, so every number is identical on any machine running the same PHP version, and every function in core is ranked by cost.

Cost class
Trivial

Touches nothing outside its own arguments.

Scaling
Constant

No loop in the body: the same number of instructions runs whatever you pass in.

Instructions
27–33

Executed per call on PHP 8.5, depending on the branch taken. The body compiles to 33.

Plugin surface
1 hook

Third-party callbacks on 'nonce_user_logged_out' run inside this call, and their cost is not bounded by anything here.

Called by
50

50 places in core call this, so the cost is paid more often than your own code shows.

What it touches

  • hookthird-party callbacksapply_filters()called directly

Further down the call graph this can also reach option, cache, serialize, transient and query. Those are the worst case, several calls deep and usually down an error path, not what a normal call pays.

What one call costs · 2 distinct outcomes

One number would be a lie: the work depends on which branch runs. These are every distinct cost wp_create_nonce() can have, taken from its control-flow graph on PHP 8.5.

WhenInstructionsCalls it makes
always27wp_get_current_user(), wp_get_session_token(), wp_nonce_tick(), wp_hash()
always33wp_get_current_user(), apply_filters(), wp_get_session_token(), wp_nonce_tick(), wp_hash()

Across PHP versions

PHPCompiledExecutedBranchesNotes
8.6-dev3327–331
8.53327–331
8.43327–3313 fewer instructions than PHP 8.3
8.33630–361
8.23630–361
8.13630–361
7.43630–361

An instruction is not a fixed amount of time, so a matching count is not necessarily the same speed; what it rules out is a difference in the work itself.

Hooks and filters fired · 1

One hook fires while wp_create_nonce() runs, in this order:

  1. apply_filters( nonce_user_logged_out )filterline 2536 (+5 into the body)

    Filters whether the user who generated the nonce is logged out.

Uses · 5

Used by · 50

Show all 50

Source code

	function wp_create_nonce( $action = -1 ) {		$user = wp_get_current_user();		$uid  = (int) $user->ID;		if ( ! $uid ) {			/** This filter is documented in wp-includes/pluggable.php */			$uid = apply_filters( 'nonce_user_logged_out', $uid, $action );		} 		$token = wp_get_session_token();		$i     = wp_nonce_tick( $action ); 		return substr( wp_hash( $i . '|' . $action . '|' . $uid . '|' . $token, 'nonce' ), -12, 10 );	}

Changelog

Introduced in 2.0.3. Unchanged from 6.7.7 through 7.1.0.

  1. 6.7.7
  2. 6.8.8
  3. 6.9.7
  4. 7.0.4
  5. 7.1.0

Signature, return type and hooks compared across 5 parsed releases.

4.0.0
Session tokens were integrated with nonce creation.from the docblock
2.0.3
Introduced.from the docblock

About this page

Parsed data
Generated from the wordpress-develop 6.9.7 tag, from src/wp-includes/pluggable.php, and regenerated for each WordPress release so it tracks the code rather than a snapshot of it.
Corrections
Something wrong on this page? Report it and it gets fixed in the next regeneration.