Skip to content

Fix Google Apps Script Web App Latency and Redirect Delays

The Phantom Latency: Why Fast Scripts Feel Slow

Section titled “The Phantom Latency: Why Fast Scripts Feel Slow”

In advanced workflow automation, a common bottleneck occurs when a Google Apps Script (GAS) Web App is triggered via a POST or GET request. The internal Executions log reports a runtime of 1.2 seconds, yet the client-side application (Postman, a React frontend, or a low-code tool) hangs for 45 to 59 seconds before receiving a response.

This discrepancy is rarely caused by the logic within your functions. Instead, it is typically triggered by “Invisible Overhead”—specifically, the way Google handles Project Libraries and the 302 Redirect handshake between script.google.com and script.googleusercontent.com.

The solution is two-pronged:

  1. Eliminate Synchronous Library Bloat: Every library attached to your script via the Editor > Libraries section is loaded into memory before the doGet or doPost function triggers, but after the request is received. This initialization time is often omitted from the Execution Log.
  2. Bypass HtmlService for Data: Using HtmlService.createHtmlOutput() for API responses forces Google to wrap your data in a complex sandbox. Switching to ContentService with a specific MIME type avoids the heavy UI lifting and streamlines the redirect.

If your script relies on large external libraries (like OAuth2 or custom internal utility scripts), the engine must fetch and compile them on every “cold start” of the web app.

Action Optimization Impact
Libraries Remove unnecessary libraries in Project Settings. Reduces cold-start by 5-10s.
Code Structure Copy essential library code directly into a new .gs file. Eliminates external fetch latency.
Global Variables Move heavy initializations inside the doPost scope. Prevents execution on every trigger.

2. Implement the Lightweight JSON Dispatcher

Section titled “2. Implement the Lightweight JSON Dispatcher”

Replace any HtmlService output with ContentService. This ensures the client receives a raw string, reducing the processing time Google spends “sanitizing” the response for a browser.

In your Code.gs file:

function doPost(e) {
try {
// 1. Logic Execution
const result = processAutomation(e.postData.contents);
// 2. Optimized Output
return ContentService.createTextOutput(JSON.stringify(result))
.setMimeType(ContentService.MimeType.JSON);
} catch (err) {
return ContentService.createTextOutput(JSON.stringify({ "status": "error", "message": err.message }))
.setMimeType(ContentService.MimeType.JSON);
}
}

The 30-59s delay is often the client waiting for the redirect. When you call a GAS URL, Google sends a 302 Moved Temporarily status. If your client is not configured to follow redirects automatically, the handshake may time out.

Required JSON Structure for Request: When sending data to your optimized script, ensure your headers match the expected input to prevent “pre-flight” OPTIONS request delays.

{
"action": "sync_data",
"payload": {
"userId": "12345",
"timestamp": "2023-10-27T10:00:00Z"
}
}

Fetch Settings (JavaScript Example):

fetch('YOUR_SCRIPT_URL', {
method: 'POST',
redirect: 'follow', // Crucial: This prevents the 30s hang on the 302 redirect
body: JSON.stringify(data),
headers: {
'Content-Type': 'text/plain;charset=utf-8'
// Note: Using text/plain avoids the 'OPTIONS' pre-flight check in GAS
}
});
  1. Open your script and click Deploy > New Deployment.
  2. Select Type: Web App.
  3. Ensure Execute as: is set to “Me” and Who has access: is set to “Anyone” (standard for webhooks).
  4. Copy the new URL. Important: Always use the /exec endpoint, never the /dev endpoint for production traffic, as /dev is significantly slower due to authentication checks.
  • The 50MB Response Limit: ContentService cannot return strings larger than 50MB. If your “1s execution” is generating a massive dataset, the delay is the network serialization. In this case, upload the data to a Google Drive file and return the File ID instead.
  • Concurrent Request Quotas: If you are hitting the script with more than 30 concurrent users, Google will throttle the entry point. The script won’t even “start,” causing a client-side timeout while the Execution Log shows nothing. To solve this, use a Google Cloud Pub/Sub or a Firebase Realtime Database as a buffer.
  • V8 Engine Latency: Ensure your script is running on the V8 Runtime (Project Settings > Chrome V8 Runtime). Legacy Rhino engines handle library imports much slower, exacerbating the “hidden” delay.