Handling POST Requests the WordPress Way

Share this article

Get the Right File Permissions in Your WordPress Site with our tutorial.

An interactive website needs to be able to interact with user input, which is commonly in the form of form submissions. A WordPress site is no exception to this rule. There are various interactions happening on a website on a daily basis. For example, subscribing to a newsletter, sending a message to the site owner and filling in an order form. All of these usually happen from POST requests during a form submission.

WordPress Transients API

In this article, we’re going to take a simple example of handling a POST request from a user, which is submitted via a contact form. We are going to use an internal WordPress hook to properly get the data, process it accordingly and redirect the user back to a specific page.

This article assumes you have a basic knowledge of the WordPress Plugin API. If you’re not familiar, it’s highly recommended to review the Codex page first.

The Background

WordPress is based on an event driven architecture. This means internally, WordPress core is filled up with various actions and filters to modify the program execution or to alter the content during runtime. Examples of actions that are running during program execution are init, wp, template_redirect and wp_head. Many plugins utilise these actions and filters to modify how WordPress works.

It is no different with what we are going to achieve. All we need to know is the proper hooks needed for the POST request and to modify our code accordingly. This is possible by pointing all our form submissions to a specific file in the wp-admin directory called admin-post.php. If you have experienced integrating your application with WordPress internal AJAX API, then you will notice that the basic structure of admin-post.php is not much different to the admin-ajax.php counterpart.

Anatomy of admin-post.php

At the very basic level, admin-post.php only contains 71 lines of code. It starts by defining WP_ADMIN constant and then loading WordPress by requiring wp-load.php. After that it sends an appropriate header and triggers the admin_init action.

The current action is determined by this line of code:

$action = empty( $_REQUEST['action'] ) ? '' : $_REQUEST['action'];

Despite the name, admin-post.php can actually handle both of POST and GET requests. However, what we’re interested to go through in the context of this article will be only related to POST request. Next,

if ( ! wp_validate_auth_cookie() ) {
    if ( empty( $action ) ) {
        /**
         * Fires on a non-authenticated admin post request where no action was supplied.
         *
         * @since 2.6.0
         */
        do_action( 'admin_post_nopriv' );
    } else {
        /**
         * Fires on a non-authenticated admin post request for the given action.
         *
         * The dynamic portion of the hook name, `$action`, refers to the given
         * request action.
         *
         * @since 2.6.0
         */
        do_action( "admin_post_nopriv_{$action}" );
    }
} else {
    if ( empty( $action ) ) {
        /**
         * Fires on an authenticated admin post request where no action was supplied.
         *
         * @since 2.6.0
         */
        do_action( 'admin_post' );
    } else {
        /**
         * Fires on an authenticated admin post request for the given action.
         *
         * The dynamic portion of the hook name, `$action`, refers to the given
         * request action.
         *
         * @since 2.6.0
         */
        do_action( "admin_post_{$action}" );
    }
}

We can see that two different action hooks will be triggered based on the user logged in status which is admin_post for logged in user and admin_post_nopriv for the non logged in user. If you need a more refined control to only processing the data related to your request, a more specific action will also be triggered, namely admin_post_nopriv_{$action} and admin_post_{$action}, again based on the logged in state of the user.

Let’s say our POST request has an action with a value of foobar. These two actions will be triggered if the current user is not logged in:

  • admin_post_nopriv
  • admin_post_nopriv_foobar

If you are currently logged in, two different actions will be triggered:

  • admin_post
  • admin_post_foobar

For GET request, when you are accessing the URL like this:

http://www.example.com/wp-admin/admin-post.php?action=foobar&data=test

All the above four hooks are still available to you to use.

With this knowledge, we can easily hook into the appropriate actions to handle the contact form submission without actually messing with our theme template.

Proof of Concept

Let’s take a contact form as an example. The simplest way to do this (not recommended) is perhaps to create a custom page template based on the basic page.php, hardcode a form and do the processing in that file itself. This is roughly how it looks:

<?php
/*
 * Template Name: Contact Page
 */

get_header();

if ( ! empty( $_POST ) ) {
    // Sanitize the POST field
    // Generate email content
    // Send to appropriate email
}

?>

<div id="content">
    <form action="" method="post">
        <label for="fullname">Full Name</label>
        <input type="text" name="fullname" id="fullname" required>
        <label for="email">Email Address</label>
        <input type="email" name="email" id="email" required>
        <label for="message">Your Message</label>
        <textarea name="message" id="message"></textarea>
        <input type="submit" value="Send My Message">
    </form>
</div>

<?php
get_footer();

While this implementation will work, there is no separation or concern whatsoever which will lead to trouble in the long run. Maintaining the form is a nightmare as the processing is only happening in one place, and if we are to duplicate the form somewhere else, we need to redo the processing again.

To fully utilize the event driven nature of WordPress, and also to provide a separation of concern, we can make use of the admin-post.php provided. Converting the existing form to make it compatible with admin-post.php is fairly straightforward too.

We first change this line from:

<form action="" method="post">

to:

<form action="<?php echo esc_url( admin_url('admin-post.php') ); ?>" method="post">

To generate a proper URL pointing to the admin-post.php, we use the built-in function admin_url. This will make sure that our URL is always correct in reference to the current site that is running.

We also need to add the action hidden input so that we can trigger the more specific hook related to our contact form submission. Let’s use an example of contact_form for the action value.

We add this line somewhere in between the <form> tag.

<input type="hidden" name="action" value="contact_form">

This is how the page template looks like after the modification:

<?php
/*
 * Template Name: Contact Page
 */

get_header();
?>

<div id="content">
    <form action="<?php echo esc_url( admin_url('admin-post.php') ); ?>" method="post">
        <label for="fullname">Full Name</label>
        <input type="text" name="fullname" id="fullname" required>
        <label for="email">Email Address</label>
        <input type="email" name="email" id="email" required>
        <label for="message">Your Message</label>
        <textarea name="message" id="message"></textarea>
        <input type="hidden" name="action" value="contact_form">
        <input type="submit" value="Send My Message">
    </form>
</div>

<?php
get_footer();

Notice that we are removing the POST processing function on top of the template since we are going to hook into the action later on.

Actual Handling of the POST Request

For this part, we have two options, and going with either one is fine. We can either hook into the admin_post_* action via the functions.php of our theme, or we can create a simple plugin to handle the contact form. Let’s just code this into the functions.php for simplicity sake.

Remember that we have our custom action contact_form in the form, so by rights, we will have these four hooks available to us:

  • admin_post_nopriv
  • admin_post_nopriv_contact_form
  • admin_post
  • admin_post_contact_form

Open up functions.php of your current theme and add these lines in:

function prefix_send_email_to_admin() {
    /**
     * At this point, $_GET/$_POST variable are available
     *
     * We can do our normal processing here
     */ 

    // Sanitize the POST field
    // Generate email content
    // Send to appropriate email
}
add_action( 'admin_post_nopriv_contact_form', 'prefix_send_email_to_admin` );
add_action( 'admin_post_contact_form', 'prefix_send_email_to_admin` );

Since we want to handle the form submission the same way no matter if the user is currently logged in or not, we point to the same callback function for both admin_post_nopriv_contact_form and admin_post_contact_form. The actual callback function, prefix_send_email_to_admin will be your main point to do the data processing.

Conclusion

admin-post.php is a useful, hidden gem contained in the WordPress core. By utilizing it, we can keep the separation of concern between the actual template, and unrelated code that is not needed for display separated by hooking into the appropriate hooks for processing.

Get the Right File Permissions in Your WordPress Site with our tutorial.

Frequently Asked Questions (FAQs) about Handling POST Requests the WordPress Way

What is a POST request in WordPress?

A POST request in WordPress is a method used to send data to a server from a web page. It is commonly used in forms where the user provides information that is then sent to the server for processing. The data sent via a POST request is not visible in the URL, making it a secure way to send sensitive information like passwords or credit card numbers.

How do I handle POST requests in WordPress?

Handling POST requests in WordPress involves using the ‘wp_remote_post()’ function. This function allows you to send POST requests to a specified URL and receive a response. The function takes two parameters: the URL to which the request is sent, and an array of arguments that can include headers, body content, and more.

What is the difference between GET and POST requests in WordPress?

The main difference between GET and POST requests in WordPress lies in how data is sent and received. With a GET request, data is appended to the URL and is visible in the browser’s address bar. On the other hand, a POST request sends data in the body of the HTTP request, making it invisible in the URL and more secure for transmitting sensitive data.

How can I send data using a POST request in WordPress?

To send data using a POST request in WordPress, you need to use the ‘wp_remote_post()’ function with the URL and an array of arguments. The ‘body’ argument in the array is where you can include the data you want to send. This data should be in an associative array format, with key-value pairs representing the data fields and their values.

How do I handle the response from a POST request in WordPress?

After sending a POST request in WordPress using the ‘wp_remote_post()’ function, you can handle the response using the ‘wp_remote_retrieve_body()’ function. This function extracts the body content from the response, allowing you to process or display it as needed.

Can I send POST requests to external services in WordPress?

Yes, you can send POST requests to external services in WordPress. The ‘wp_remote_post()’ function allows you to specify any URL as the target of the request, including URLs of external services. This makes it possible to interact with APIs, submit forms to other websites, and more.

What are the common issues when handling POST requests in WordPress?

Some common issues when handling POST requests in WordPress include incorrect URL, incorrect data format in the ‘body’ argument, and server-side issues like timeouts or errors. It’s important to check the response for any error messages and handle them appropriately.

How can I debug POST requests in WordPress?

Debugging POST requests in WordPress can be done by examining the response from the ‘wp_remote_post()’ function. If the function returns a WP_Error object, you can use the ‘get_error_message()’ method to get a detailed error message. You can also use various debugging tools and plugins available for WordPress.

Can I send files using POST requests in WordPress?

Yes, you can send files using POST requests in WordPress. The ‘body’ argument of the ‘wp_remote_post()’ function can include file data. However, the file data must be properly formatted and the target URL must be able to handle file uploads.

How can I secure my POST requests in WordPress?

Securing POST requests in WordPress can be done by using HTTPS for the request URL, sanitizing and validating data before sending it, and checking for and handling any errors in the response. It’s also important to use nonces in your forms to protect against cross-site request forgery attacks.

Firdaus ZahariFirdaus Zahari
View Author

Firdaus Zahari is a web developer who comes all the way from Malaysia. His passion revolves around (but is not limited to) WordPress and front-end development.

ChrisBPOSTPOST requestsWordPress
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week