Skip to content

Hooks

Introduction

This article covers descriptions and explanations about hooks in WordPress code. Hooks are a way for one piece of code to interact with or modify another piece of code at specific, pre-defined spots. They make up the foundation for how plugins and themes interact with WordPress Core, but they’re also used extensively by Core itself.

There are two types of hooks: Actions and Filters. To use either, you need to write a custom function known as a Callback, and then register it with a WordPress hook for a specific action or filter.

init

The init hook runs after the WordPress environment is loaded but before the current request is processed. This hook also allows developers to register custom post types and taxonomies, or perform other tasks that need to be executed early in the WordPress loading process.

A lot of the time, developers attach a function to this hook to view or process sensitive data without a proper permission and nonce check.

This hook itself is accessible by unauthenticated users by default (also depends on whether the hook is registered outside of an additional permission check). Visiting the front page of a WordPress site should trigger the init hook.

An unauthenticated user can visit the front page of a WordPress instance, and it will trigger any function that is attached to the init hook.

Example of hook implementation:

add_action( 'init', 'process_post' );
function process_post() {
if( isset( $_POST['unique_hidden_field'] ) ) {
// process $_POST data here, possibly need to add permission and nonce check first
}
}

admin_init

The admin_init hook is commonly used by developers to perform various tasks when the WordPress admin panel is loaded. These tasks can include adding custom menus, registering custom post types or taxonomies, initializing settings, and performing security checks or authentication for admin-specific actions.

This hook is similar to the init hook, but it only fires as an admin screen or script is being initialized. This hook does not just run on user-facing admin screens; it also runs on the admin-ajax.php and admin-post.php endpoints as well.

Example of hook implementation:

function myplugin_settings() {
register_setting( 'myplugin', 'myplugin_setting_1', 'intval' );
register_setting( 'myplugin', 'myplugin_setting_2', 'intval' );
}
add_action( 'admin_init', 'myplugin_settings' );

Since this hook also runs on the admin-ajax.php and admin-post.php endpoints, an unauthenticated user can trigger this hook by visiting the URL below:

Terminal window
curl <WORDPRESS_BASE_URL>/wp-admin/admin-ajax.php?action=heartbeat

wp_ajax_{$action}

This hook allows developers to handle custom AJAX endpoints. The wp_ajax_ hooks follow the format wp_ajax_$action, where the $action variable comes from the action GET/POST parameter submitted to the admin-ajax.php endpoint.

This hook only fires for logged-in users, so by default, only users with the Subscriber+ role can access the attached function on the hook. A proper permission and nonce check is still needed to secure the function attached to this hook.

Example of hook implementation:

add_action( 'wp_ajax_foobar', 'my_ajax_foobar_handler' );
function my_ajax_foobar_handler() {
// Make your response and echo it.
// Don't forget to stop execution afterward.
wp_die();
}

wp_ajax_nopriv_{$action}

This hook is functionally the same as wp_ajax_{$action}, except the nopriv variant is used for handling AJAX requests from unauthenticated users, i.e., when the is_user_logged_in() function returns false.

admin_action_{$action}

This hook is equivalent to wp_ajax_ hook, but on a different endpoint. Similar to wp_ajax_, the admin_action hooks follow the format admin_action_$action, where the $action variable comes from the action GET/POST parameter. The only difference is that the URL to hit the request must be to the /wp-admin/admin.php endpoint.

This hook only fires for logged-in users, so by default, only users with the Subscriber+ role can access the attached function on the hook. A proper permission and nonce check is still needed to secure the function attached to this hook.

Example of hook implementation:

add_action( 'admin_action_foobar', 'my_ajax_foobar_handler' );
function my_ajax_foobar_handler() {
echo "Execution successful";
wp_die();
}

A logged-in user can send the following request execute the hook.

Terminal window
curl <WORDPRESS_BASE_URL>/wp-admin/admin.php?action=foobar

admin_post_{$action}

This hook is equivalent to both wp_ajax_ and admin_action_ hooks but on a different endpoint. Similar to both hooks, the admin_post_ hooks follow the format admin_post_$action, where the $action variable comes from the action GET/POST parameter. The only difference is that the URL to hit the request must be to the /wp-admin/admin-post.php endpoint.

This hook only fires for logged-in users, so by default, only users with the Subscriber+ role can access the attached function on the hook. A proper permission and nonce check is still needed to secure the function attached to this hook.

Example of hook implementation:

add_action( 'admin_post_foobar', 'admin_post_handler' );
function admin_post_handler() {
// Make your response and echo it.
// Don't forget to stop execution afterward.
wp_die();
}

A logged-in user can send the following request execute the hook.

Terminal window
curl <WORDPRESS_BASE_URL>/wp-admin/admin-post.php?action=foobar

admin_post_nopriv_{$action}

This hook is functionally the same as admin_post_{$action}, except the nopriv variant is used for handling AJAX requests from unauthenticated users, i.e., when the is_user_logged_in() function returns false.

template_redirect

The template_redirect is used for cases when a feature needs to be implemented right after querying the WP site, but before determining which template to load.

This hook fires after loading the homepage of the WordPress site.

Example of hook implementation:

add_action( 'template_redirect', 'my_ajax_foobar_handler' );
function my_ajax_foobar_handler() {
if ( isset( $_GET['unique_hidden_field'] ) ) {
echo "Execution successful";
wp_die();
}
}

An unauthenticated user can send the following request to execute the hook.

Terminal window
curl <WORDPRESS_BASE_URL>/?unique_hidden_field=1

Below are some of the vulnerabilities that acted as a starting point from the template_redirect hook:

admin_notices

The admin_notices hook is used to add custom notices in the WordPress admin dashboard. These notices can serve various purposes, such as warning users about missing settings, confirming successful actions, or promoting premium features. This hook fires on loading the WordPress admin dashboard /wp-admin page.

Example of hook implementation:

add_action( 'admin_notices', 'admin_notice_handler' );
function admin_notice_handler() {
echo '<div class="notice notice-success"><p>' . $_GET['notice'] . '</p></div>';
wp_die();
}

An unauthenticated user can send the following request to execute the hook.

Terminal window
curl <WORDPRESS_BASE_URL>/?notice=XSS_PAYLOAD

Contributors

rafiemdhakalananda