Contact Form sent via Ajax with file upload returns 302 instead of 200 status code I’m using the Contact Form plugin to submit a form that can also upload files via Ajax. The problem ist that instead of a success (200) I get a status code of 302. But I receive the resulting mail properly.

Everything worked fine when I hadn’t set everything up for the file upload. I serialized the form data, expected json, success (200).

...
dataType: 'json',
data: $(this).serialize(),
...


But when I switched to this to enable receiving files, things went south (302):

const formData = new FormData(this.form);

$.post({
    url: '/index.php',
    data: formData,
    type: 'POST',
    cache: false,
    contentType: false,
    processData: false,

    success: response =&gt; {
        if (response.success) {
            this.showSuccess();
        } else {
            this.showError();
        }
    },
    error: (xhr) =&gt; {
        // FIXME: This is a workaround because for some reason Craft returns a 302, even though everything works
        //        fine. See stackoverflow question here and fix if there is a solution.
        if (xhr.status === 302) {
        this.showSuccess();
        } else {
        this.showError();
        }
    }
});


I’m on Craft 3.0.15 and set up SMTP in Settings -> EMail. I tried it locally (using MAMP) and on my hosted server. Both give me the 302.

Any hint would be much appreciated. 

Cheers,
Mark
Solved it (finally).

First part of the problem (the 302 redirect) was this missing configuration in the ajax call:

$.post({
  // ...
  dataType: 'json',
  // ...
};


It tells the server what you expect to get back from it.

Now this was fine, but I ran into a second issue. The xhr request stalled in Safari. When that happend and I reloaded the page (causing the request to finally stop), the email was sent anyways. The reason:

Safari seems to have an issue with this:

const formData = new FormData(formElement);


I found a hint here https://stackoverflow.com/a/8797289/2342963 that fixes the issue: appending the data manually to the FormData instance instead of passing the form element to the constructor.

So this is the working code:

const formData = new FormData();

$.each($('input, textarea, select'), function(i, element) {
    if (element.files) {
      $.each(element.files, function(i, file) {
        formData.append(element.name, file);
      });
    } else {
      formData.append(element.name, element.value);
    }
});

$.post({
    url: '/',
    data: formData,
    type: 'POST',
    cache: false,
    contentType: false,
    processData: false,
    dataType: 'json',

    success: response =&gt; {
        if (response.success) {
        // Show success
        } else {
        // Show error
        }
    },
    error: () =&gt; {
        // Show error
    }
});


Turns out, it wasn’t a Craft issue after all ¯_(ツ)_/¯