I have a Google Apps Script project acting as a webhook. When calling the endpoint using a library like htmx, the preflight check fails and the request subsequently fails. When calling directly with fetch
or XMLHttpRequest
, it works fine.
I have a sample endpoint with a simple doPost
for testing:
const doPost = (request = {}) => {
const { postData: { contents, type } = {} } = request;
return ContentService.createTextOutput(contents);
};
This Codepen sample shows how requests with HTMX fail while fetch
and XHRHttpRequest
are successful.
Some things I've learned:
OPTIONS
header sent in a preflight results in a 405 error, aborting the request entirely. You can mimic this by sending an OPTIONS
request via Postman (or similar) to the web app URL.Access-Control-Allow-Origin
header, which is what shows as the failure reason in the console.In this kind of situation, what is the best method for debugging? I'm not really sure what else to try to get this working.
This is an issue with HTMX, that requires modification of its source code. The source of the problem is that HTMX adds some event listeners to xhr.upload
that makes the browsers mark the request as "not simple", triggering the CORS preflight request:
If the request is made using an XMLHttpRequest object, no event listeners are registered on the object returned by the XMLHttpRequest.upload property used in the request; that is, given an XMLHttpRequest instance xhr, no code has called xhr.upload.addEventListener() to add an event listener to monitor the upload
The specific part of HTMX source code:
forEach(['loadstart', 'loadend', 'progress', 'abort'], function(eventName) {
forEach([xhr, xhr.upload], function (target) {
target.addEventListener(eventName, function(event){
triggerEvent(elt, "htmx:xhr:" + eventName, {
lengthComputable:event.lengthComputable,
loaded:event.loaded,
total:event.total
});
})
});
});
Sadly, the addEventListener
uses anonymous functions, so there's no way to remove them with removeEventListener
AFAIK.
But if you are willing to use a custom HTMX script in your app until the authors fix this issue (e.g. add ability to prevent event listener creation on xhr.upload
), just remove the xhr.upload
from the list in the second row:
forEach(['loadstart', 'loadend', 'progress', 'abort'], function(eventName) {
forEach([xhr], function (target) {
target.addEventListener(eventName, function(event){
triggerEvent(elt, "htmx:xhr:" + eventName, {
lengthComputable:event.lengthComputable,
loaded:event.loaded,
total:event.total
});
})
});
});
With this modification your original suggestion of removing non-standard HTMX-specific headers via evt.detail.headers = []
will work, since now this request becomes "simple", so no more CORS preflight is made by the browsers.
Note: the modification may break the HTMX file upload, I did not test it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With