WordPress AJAX $_POST data not being received from JavaScript FormData

I’m having trouble with my WordPress AJAX setup. The $_POST variable is empty when I send FormData through JavaScript, but it works fine with regular strings.

PHP handler code:

add_action('wp_ajax_process_content', 'handle_content_submission');
add_action('wp_ajax_nopriv_process_content', 'handle_content_submission');

function handle_content_submission() {
    if (isset($_POST["user_data"])) {
        $content = $_POST["user_data"];
    } else {
        $content = 'default_value';
    }
    var_dump($_POST);
    wp_die();
}

JavaScript function:

function processContent() {
    alert('starting process');
    var textArea = document.getElementById('content_area');
    var tempDiv = document.createElement('div');
    tempDiv.innerHTML = textArea.innerHTML;
    var imageList = Array.prototype.slice.call(tempDiv.getElementsByTagName('img'));
    var submissionData = new FormData();
    
    for (var j = 0; j < imageList.length; j++) {
        var placeholder = document.createTextNode("img_" + j);
        imageList[j].parentNode.insertBefore(placeholder, imageList[j]);
        imageList[j].parentNode.removeChild(imageList[j]);
        submissionData.append('photos[]', imageList[j].src);
    }
    
    submissionData.append('content', tempDiv.innerHTML);
    
    jQuery.ajax({
        url: 'admin-ajax.php',
        method: 'POST',
        data: {
            'action': "process_content",
            'user_data': submissionData
        },
        timeout: 8000
    }).done(function(response) {
        alert(response);
    }).fail(function() {
        alert('request failed');
    });
}

The var_dump doesn’t show anything and the alert only displays ‘starting process’ then stops. When I replace the FormData with a simple string, everything works perfectly. What’s causing this issue with FormData in WordPress AJAX calls?

You’re nesting FormData inside a regular object, which breaks everything. When you do data: { 'action': "process_content", 'user_data': submissionData }, jQuery can’t serialize the FormData properly. Instead, append the action directly to your FormData: submissionData.append('action', 'process_content'); then use data: submissionData in your AJAX call. Don’t forget processData: false and contentType: false in your jQuery options - without these, jQuery will butcher the encoding.

formData can be tricky. try passing it directly like this: submissionData.append(‘action’, ‘process_content’); and then send the whole submissionData. that usually fixes the issue with empty $_POST.

You’re mixing FormData with a regular object structure - that’s what’s breaking it. I hit this same issue last year building file uploads. When you wrap your FormData in { 'action': ..., 'user_data': submissionData }, jQuery tries to serialize everything as URL-encoded data and corrupts the FormData. Instead, add your action directly to the FormData with submissionData.append('action', 'process_content') and pass that FormData straight to the data parameter. Don’t forget processData: false and contentType: false in your AJAX options - without these, jQuery messes with your FormData and mangles the multipart boundary. Your server ends up getting garbage.