Google Apps Script Web App doPost Consistently Returns HTTP 405 Method Not Allowed with e Object undefined

415 views
Skip to first unread message

Leylani Ferniza

unread,
Jun 24, 2025, 4:22:23 AMJun 24
to Google Apps Script Community
Problem
  • My Google Apps Script Web App, which includes a doPost(e) function designed to handle incoming POST requests with JSON data, is consistently failing to receive the POST data. When invoked via curl, the initial request receives a 302 Redirect, and the subsequent request to the script.googleusercontent.com URL results in an HTTP 405 Method Not Allowed error from Google's servers. Crucially, the Apps Script execution logs show that the doPost function is indeed triggered, but the e (event) object is undefined.
Expectation: 
  • A Google Apps Script Web App with a doPost function, deployed with "Execute as: Me" and "Who has access: Anyone," should correctly accept POST requests. The e object within the doPost function should contain the incoming data (e.postData or e.parameter), allowing the script to process it and return its intended response (ContentService.createTextOutput).

Observed Behavior:
  1. curl Response:

  2. Apps Script Execution Log:

    • The doPost(e) function in the Apps Script is executed.
    • However, Logger.log("doPost received event: " + JSON.stringify(e)); consistently shows: doPost received event: undefined. This indicates that the event object, which should contain the request's parameters and body, is not being passed to the function, or is being stripped/lost by the Apps Script runtime environment.
Steps to reproduce: 
  1. Deploy the Apps Script (provided below) as a "Web app" with "Execute as: Me" and "Who has access: Anyone."

  2. Obtain the Web App URL (e.g., https://script.google.com/macros/s/AKfycbxB0ZSHbpYq8h-oaMkKd4Y6oFAKtEKOpWi07JjsZJS8G1HIc2o5zcirmJbusRl-Wz7ACg/exec).

  3. Execute the following curl command from a terminal:

curl -v -L -X POST "https://script.google.com/macros/s/AKfycbxB0ZSHbpYq8h-oaMkKd4Y6oFAKtEKOpWi07JjsZJS8G1HIc2o5zcirmJbusRl-Wz7ACg/exec" \
  -H "Content-Type: application/json" \
  --data-binary '{
    "foldername": "TestFolder",
    "filename": "test.jpg",
    "image": "data:image/jpeg;base64,dGVzdA=="
  }'

Troubleshooting Performed:
  • Verified doPost Function: The doPost(e) function is correctly defined and structured to handle both e.parameter (for form-urlencoded) and e.postData.contents (for JSON). Its syntax is valid.
  • Confirmed Deployment Settings: The Web App is consistently deployed as "Type: Web app", "Execute as: Me", and "Who has access: Anyone".
  • Tested curl Flags:
    • Used -L to follow redirects.
    • Used --location-trusted to attempt to resend headers on redirect.
    • Explicitly set the method with -X POST.
    • Used the correct Content-Type: application/json header.
    • Used --data-binary to ensure the raw POST body is sent, especially on redirects.
  • Attempted Direct POST to Redirected URL: Manually extracted the script.googleusercontent.com URL from the Location header and attempted a direct POST to it. This also resulted in a 405 Method Not Allowed.
  • Created New Deployments: Tested the same script with multiple new deployments, always exhibiting the identical problematic behavior.

Relevant Data:

  • Apps Script Project ID: 1pyX1dfST84v6b5ICYw6o-Yt2H8d9lmpQTLeVYtLYxbUWO7v8tusdpYcx

  • Current Apps Script Deployment ID: AKfycbxB0ZSHbpYq8h-oaMkKd4Y6oFAKtEKOpWi07JjsZJS8G1HIc2o5zcirmJbusRl-Wz7ACg

  • Apps Script Code.gs (relevant doPost function):

function doPost(e) {
try {
// Log the entire event object to inspect its structure
Logger.log("doPost received event: " + JSON.stringify(e));

// Try to get data from parameter (for form-urlencoded) or postData (for JSON/raw)
var folderName = e.parameter.foldername || (e.postData && JSON.parse(e.postData.contents).foldername);
var fileName = e.parameter.filename || (e.postData && JSON.parse(e.postData.contents).filename);
var imageData = e.parameter.image || (e.postData && JSON.parse(e.postData.contents).image);

if (!folderName || !fileName || !imageData) {
return ContentService.createTextOutput("Missing data: foldername, filename, or image are missing. Received: " + JSON.stringify(e.parameter) + " || " + (e.postData ? e.postData.contents : "No postData"));
}

// Original image data processing (assuming it's a data URI)
var matches = imageData.match(/^data:([A-Za-z-+/]+);base64,(.+)$/);
if (!matches || matches.length !== 3) {
return ContentService.createTextOutput("Invalid image data format. Expected data URI. Received: " + imageData);
}

var contentType = matches[1];
var base64Data = matches[2];
var bytes = Utilities.base64Decode(base64Data);
var blob = Utilities.newBlob(bytes, contentType, fileName);

var folders = DriveApp.getFoldersByName(folderName);
var folder = folders.hasNext() ? folders.next() : DriveApp.createFolder(folderName);

folder.createFile(blob);

return ContentService.createTextOutput("SUCCESS - File created: " + fileName + " in " + folderName);

} catch (err) {
return ContentService.createTextOutput("ERROR: " + err.message + " Stack: " + err.stack);
}
}

curl -v Output:

curl -v -L -X POST "https://script.google.com/macros/s/AKfycbxB0ZSHbpYq8h-oaMkKd4Y6oFAKtEKOpWi07JjsZJS8G1HIc2o5zcirmJbusRl-Wz7ACg/exec" \

>   -H "Content-Type: application/json" \

>   --data-binary '{

>     "foldername": "TestFolder",

>     "filename": "test.jpg",

>     "image": "data:image/jpeg;base64,dGVzdA=="

>   }'

Note: Unnecessary use of -X or --request, POST is already inferred.

* Host script.google.com:443 was resolved.

* IPv6: (none)

* IPv4: 142.250.186.110

*   Trying 142.250.186.110:443...

* Connected to script.google.com (142.250.186.110) port 443

* ALPN: curl offers h2,http/1.1

* (304) (OUT), TLS handshake, Client hello (1):

*  CAfile: /etc/ssl/cert.pem

*  CApath: none

* (304) (IN), TLS handshake, Server hello (2):

* (304) (IN), TLS handshake, Unknown (8):

* (304) (IN), TLS handshake, Certificate (11):

* (304) (IN), TLS handshake, CERT verify (15):

* (304) (IN), TLS handshake, Finished (20):

* (304) (OUT), TLS handshake, Finished (20):

* SSL connection using TLSv1.3 / AEAD-CHACHA20-POLY1305-SHA256 / [blank] / UNDEF

* ALPN: server accepted h2

* Server certificate:

*  subject: CN=*.google.com

*  start date: Jun  2 08:35:40 2025 GMT

*  expire date: Aug 25 08:35:39 2025 GMT

*  subjectAltName: host "script.google.com" matched cert's "*.google.com"

*  issuer: C=US; O=Google Trust Services; CN=WE2

*  SSL certificate verify ok.

* using HTTP/2

* [HTTP/2] [1] OPENED stream for https://script.google.com/macros/s/AKfycbxB0ZSHbpYq8h-oaMkKd4Y6oFAKtEKOpWi07JjsZJS8G1HIc2o5zcirmJbusRl-Wz7ACg/exec

* [HTTP/2] [1] [:method: POST]

* [HTTP/2] [1] [:scheme: https]

* [HTTP/2] [1] [:authority: script.google.com]

* [HTTP/2] [1] [:path: /macros/s/AKfycbxB0ZSHbpYq8h-oaMkKd4Y6oFAKtEKOpWi07JjsZJS8G1HIc2o5zcirmJbusRl-Wz7ACg/exec]

* [HTTP/2] [1] [user-agent: curl/8.7.1]

* [HTTP/2] [1] [accept: */*]

* [HTTP/2] [1] [content-type: application/json]

* [HTTP/2] [1] [content-length: 119]

> POST /macros/s/AKfycbxB0ZSHbpYq8h-oaMkKd4Y6oFAKtEKOpWi07JjsZJS8G1HIc2o5zcirmJbusRl-Wz7ACg/exec HTTP/2

> Host: script.google.com

> User-Agent: curl/8.7.1

> Accept: */*

> Content-Type: application/json

> Content-Length: 119

* upload completely sent off: 119 bytes

< HTTP/2 302 

< content-type: text/html; charset=UTF-8

< access-control-allow-origin: *

< cache-control: no-cache, no-store, max-age=0, must-revalidate

< pragma: no-cache

< expires: Mon, 01 Jan 1990 00:00:00 GMT

< date: Mon, 23 Jun 2025 15:28:05 GMT

* Need to rewind upload for next request

< location: https://script.googleusercontent.com/macros/echo?user_content_key=AehSKLiiJv1P2bCSd-qBFcxkMGICpQCMi6NJsBolnrlK9Ul-5Qzt5y8H99B_I9Vd_BGOKaDrNURVumxV5Emo2bwg5PEovs_4R8516Cf9yQNSPu0k9z7HlMbCAdKBYo7RTGasm8QTppsBvtWIfIHhm9uRKlOe81weAe8qcaRRqIU9CTxoJ0SB0FsdWC3XvmDFAhFj3j9bXO5xiph4fMMQ2v2hTNOSwnzD_40xlBbtBf5B_ERv_2SsyJITaOdiOSrrTZI0p1evIhsGINgNeRGoC9ZpGDPmsj7eTCoq2XYTbKAI&lib=MXS28VV5SUEdqAWBbyKePmpxotw_u4NWy

< x-content-type-options: nosniff

< x-frame-options: SAMEORIGIN

< content-security-policy: frame-ancestors 'self'

< x-xss-protection: 1; mode=block

< server: GSE

< alt-svc: h3=":443"; ma=2592000,h3-29=":443"; ma=2592000

< accept-ranges: none

< vary: Accept-Encoding

* Ignoring the response-body

* Connection #0 to host script.google.com left intact

* Issue another request to this URL: 'https://script.googleusercontent.com/macros/echo?user_content_key=AehSKLiiJv1P2bCSd-qBFcxkMGICpQCMi6NJsBolnrlK9Ul-5Qzt5y8H99B_I9Vd_BGOKaDrNURVumxV5Emo2bwg5PEovs_4R8516Cf9yQNSPu0k9z7HlMbCAdKBYo7RTGasm8QTppsBvtWIfIHhm9uRKlOe81weAe8qcaRRqIU9CTxoJ0SB0FsdWC3XvmDFAhFj3j9bXO5xiph4fMMQ2v2hTNOSwnzD_40xlBbtBf5B_ERv_2SsyJITaOdiOSrrTZI0p1evIhsGINgNeRGoC9ZpGDPmsj7eTCoq2XYTbKAI&lib=MXS28VV5SUEdqAWBbyKePmpxotw_u4NWy'

* Switch from POST to GET

* Host script.googleusercontent.com:443 was resolved.

* IPv6: (none)

* IPv4: 142.250.185.129

*   Trying 142.250.185.129:443...

* Connected to script.googleusercontent.com (142.250.185.129) port 443

* ALPN: curl offers h2,http/1.1

* (304) (OUT), TLS handshake, Client hello (1):

* (304) (IN), TLS handshake, Server hello (2):

* (304) (IN), TLS handshake, Unknown (8):

* (304) (IN), TLS handshake, Certificate (11):

* (304) (IN), TLS handshake, CERT verify (15):

* (304) (IN), TLS handshake, Finished (20):

* (304) (OUT), TLS handshake, Finished (20):

* SSL connection using TLSv1.3 / AEAD-CHACHA20-POLY1305-SHA256 / [blank] / UNDEF

* ALPN: server accepted h2

* Server certificate:

*  subject: CN=*.googleusercontent.com

*  start date: Jun  2 08:36:43 2025 GMT

*  expire date: Aug 25 08:36:42 2025 GMT

*  subjectAltName: host "script.googleusercontent.com" matched cert's "*.googleusercontent.com"

*  issuer: C=US; O=Google Trust Services; CN=WE2

*  SSL certificate verify ok.

* using HTTP/2

* [HTTP/2] [1] OPENED stream for https://script.googleusercontent.com/macros/echo?user_content_key=AehSKLiiJv1P2bCSd-qBFcxkMGICpQCMi6NJsBolnrlK9Ul-5Qzt5y8H99B_I9Vd_BGOKaDrNURVumxV5Emo2bwg5PEovs_4R8516Cf9yQNSPu0k9z7HlMbCAdKBYo7RTGasm8QTppsBvtWIfIHhm9uRKlOe81weAe8qcaRRqIU9CTxoJ0SB0FsdWC3XvmDFAhFj3j9bXO5xiph4fMMQ2v2hTNOSwnzD_40xlBbtBf5B_ERv_2SsyJITaOdiOSrrTZI0p1evIhsGINgNeRGoC9ZpGDPmsj7eTCoq2XYTbKAI&lib=MXS28VV5SUEdqAWBbyKePmpxotw_u4NWy

* [HTTP/2] [1] [:method: POST]

* [HTTP/2] [1] [:scheme: https]

* [HTTP/2] [1] [:authority: script.googleusercontent.com]

* [HTTP/2] [1] [:path: /macros/echo?user_content_key=AehSKLiiJv1P2bCSd-qBFcxkMGICpQCMi6NJsBolnrlK9Ul-5Qzt5y8H99B_I9Vd_BGOKaDrNURVumxV5Emo2bwg5PEovs_4R8516Cf9yQNSPu0k9z7HlMbCAdKBYo7RTGasm8QTppsBvtWIfIHhm9uRKlOe81weAe8qcaRRqIU9CTxoJ0SB0FsdWC3XvmDFAhFj3j9bXO5xiph4fMMQ2v2hTNOSwnzD_40xlBbtBf5B_ERv_2SsyJITaOdiOSrrTZI0p1evIhsGINgNeRGoC9ZpGDPmsj7eTCoq2XYTbKAI&lib=MXS28VV5SUEdqAWBbyKePmpxotw_u4NWy]

* [HTTP/2] [1] [user-agent: curl/8.7.1]

* [HTTP/2] [1] [accept: */*]

* [HTTP/2] [1] [content-type: application/json]

> POST /macros/echo?user_content_key=AehSKLiiJv1P2bCSd-qBFcxkMGICpQCMi6NJsBolnrlK9Ul-5Qzt5y8H99B_I9Vd_BGOKaDrNURVumxV5Emo2bwg5PEovs_4R8516Cf9yQNSPu0k9z7HlMbCAdKBYo7RTGasm8QTppsBvtWIfIHhm9uRKlOe81weAe8qcaRRqIU9CTxoJ0SB0FsdWC3XvmDFAhFj3j9bXO5xiph4fMMQ2v2hTNOSwnzD_40xlBbtBf5B_ERv_2SsyJITaOdiOSrrTZI0p1evIhsGINgNeRGoC9ZpGDPmsj7eTCoq2XYTbKAI&lib=MXS28VV5SUEdqAWBbyKePmpxotw_u4NWy HTTP/2

> Host: script.googleusercontent.com

> User-Agent: curl/8.7.1

> Accept: */*

> Content-Type: application/json

* Request completely sent off

< HTTP/2 405 

< cache-control: no-cache, no-store, max-age=0, must-revalidate

< pragma: no-cache

< expires: Mon, 01 Jan 1990 00:00:00 GMT

< date: Mon, 23 Jun 2025 15:28:06 GMT

< content-type: text/html; charset=utf-8

< referrer-policy: origin

< allow: HEAD, GET

< x-content-type-options: nosniff

< x-xss-protection: 1; mode=block

< server: GSE

< alt-svc: h3=":443"; ma=2592000,h3-29=":443"; ma=2592000

< accept-ranges: none

< vary: Accept-Encoding

* Connection #1 to host script.googleusercontent.com left intact

<!DOCTYPE html><html lang="de"><head><meta name="description" content="Textverarbeitung, Präsentationen und Tabellen im Web"><meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=0"><link rel="shortcut icon" href="//docs.google.com/favicon.ico"><title>Seite nicht gefunden</title><meta name="referrer" content="origin"><link href="//fonts.googleapis.com/css?family=Product+Sans" rel="stylesheet" type="text/css" nonce="hZgLgYCApsi9esegi2EfdQ"><style nonce="hZgLgYCApsi9esegi2EfdQ">.goog-inline-block{position:relative;display:-moz-inline-box;display:inline-block}* html .goog-inline-block{display:inline}*:first-child+html .goog-inline-block{display:inline}#drive-logo{margin:18px 0;position:absolute;white-space:nowrap}.docs-drivelogo-img{background-image:url(//ssl.gstatic.com/images/branding/googlelogo/1x/googlelogo_color_116x41dp.png);-webkit-background-size:116px 41px;background-size:116px 41px;display:inline-block;height:41px;vertical-align:bottom;width:116px}.docs-drivelogo-text{color:#000;display:inline-block;opacity:.54;text-decoration:none;font-family:"Product Sans",Arial,Helvetica,sans-serif;font-size:32px;text-rendering:optimizeLegibility;position:relative;top:-6px;left:-7px;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}@media (-webkit-min-device-pixel-ratio:1.5),(min-resolution:144dpi){.docs-drivelogo-img{background-image:url(//ssl.gstatic.com/images/branding/googlelogo/2x/googlelogo_color_116x41dp.png)}}sentinel{}</style><style type="text/css" nonce="hZgLgYCApsi9esegi2EfdQ">body {background-color: #fff; font-family: Arial,sans-serif; font-size: 13px; margin: 0; padding: 0;}a, a:link, a:visited {color: #112ABB;}</style><style type="text/css" nonce="hZgLgYCApsi9esegi2EfdQ">.errorMessage {font-size: 12pt; font-weight: bold; line-height: 150%;}</style></head><body><div id="outerContainer"><div id="innerContainer"><div style="position: absolute; top: -80px;"><div id="drive-logo"><a href="/"><span class="docs-drivelogo-img" title="Google-Logo"></span><span class="docs-drivelogo-text">&nbsp;Drive</span></a></div></div><div align="center"><p class="errorMessage" style="padding-top: 50px">Datei kann derzeit nicht geöffnet werden.</p><p> Überprüfen Sie die Adresse und versuchen Sie es erneut.</p><div style="background: #F0F6FF; border: 1px solid black; margin-top: 35px; padding: 10px 125px; width: 300px;"><p><strong>Mit Google Drive mehr erledigen </strong></p><p>Mit den Apps in Google Drive können Sie Dokumente, Tabellen, Präsentationen und vieles mehr problemlos online erstellen, speichern und freigeben.</p><p>Weitere Informationen finden Sie hier: <a href="https://drive.google.com/start/apps">drive.google.com/start/apps</a>.</p></div></div></div></div></body><style nonce="hZgLgYCApsi9esegi2EfdQ">html {height: 100%; overflow: auto;}body {height: 100%; overflow: auto;}#outerContainer {margin: auto; max-width: 750px;}#innerContainer {margin-bottom: 20px; margin-left: 40px; margin-right: 40px; margin-top: 80px; position: relative;}</style></html>Note: Unnecessary use of -X or --request, POST is already inferred.

* Could not resolve host:  

* Closing connection

curl: (6) Could not resolve host:  

Note: Unnecessary use of -X or --request, POST is already inferred.

* Could not resolve host:  

* Closing connection

curl: (6) Could not resolve host:  


Apps Script Execution Log (Example showing e as undefined):
17:40:19
Notice
Execution started
17:40:19
Info
doPost received event: undefined
17:40:19
Notice
Execution completed

Conclusion:
Based on the consistent 405 Method Not Allowed response from script.googleusercontent.com (despite a POST attempt), coupled with the Apps Script doPost function receiving an undefined event object, it strongly suggests an issue within Google's Apps Script Web App execution environment. It appears POST request data is either not being correctly preserved across the necessary 302 redirect, or the script.googleusercontent.com endpoint is not correctly configured to handle POST requests for this specific type of Apps Script deployment.


Andrew Roberts

unread,
Jun 24, 2025, 4:35:16 AMJun 24
to google-apps-sc...@googlegroups.com
Wow! Impressive analysis. Kanshi Tanaike is probably the person to go to. 


--
You received this message because you are subscribed to the Google Groups "Google Apps Script Community" group.
To unsubscribe from this group and stop receiving emails from it, send an email to google-apps-script-c...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/google-apps-script-community/6bf5eb24-6fef-46e1-a136-4eeba6334e49n%40googlegroups.com.
Message has been deleted

DimuDesigns

unread,
Jun 24, 2025, 11:03:33 AMJun 24
to Google Apps Script Community
AFAIK GAS doPost() triggers do not accept raw binary payloads. Any service that sends data to a doPost endpoint in raw binary format will cause a 405 error - that's why using the --data-binary option from curl will fail.

I assume you're setting up a doPost(e) trigger as a webhook endpoint. Note that the only payloads it can reliably process are text-based. So it is only compatible with services that sends text-based data.

If you have control over the data being sent to your doPost(e) trigger, you can refactor your code to either send a link to the image that you can load in via UrlFetchApp or convert your images to a base64 encoded string and send them that way.

If the data is being sent as a raw binary by a 3rd party (where you have no control over what that 3rd party sends), then GAS is not a viable solution.

Justin Poehnelt

unread,
Jul 9, 2025, 12:44:06 PMJul 9
to Google Apps Script Community
Reply all
Reply to author
Forward
0 new messages