Fix Google Apps Script Webhook 302 Redirect Error
Why your Telegram bot webhook returns 302 Moved Temporarily and how to fix it with one line change.
If your Google Apps Script webhook keeps returning 302 Moved Temporarily error and your Telegram bot refuses to respond, you’re probably using the wrong response method. Here’s the fix.
The Problem 🔍
When setting up a Telegram bot webhook with Google Apps Script, you might see this in your webhook info:
Last error: Wrong response from the webhook: 302 Moved Temporarily
Your doPost() function runs, but Telegram never gets a proper response. The pending updates keep piling up, and your bot stays silent.
The Cause
The culprit is ContentService.createTextOutput(). While it works for many GAS use cases, it causes redirect issues when used as a webhook response for external services like Telegram.
// ❌ This causes 302 redirect
function doPost(e) {
// ... your logic
return ContentService.createTextOutput("OK");
}
The Fix ✅
Replace ContentService.createTextOutput() with HtmlService.createHtmlOutput():
// ✅ This works
function doPost(e) {
// ... your logic
return HtmlService.createHtmlOutput("OK");
}
That’s it. One line change.
Complete Working Example
Here’s a minimal working doPost() for a Telegram webhook:
function doPost(e) {
try {
const contents = JSON.parse(e.postData.contents);
const chatId = String(contents.message.chat.id);
const text = (contents.message.text || "").toLowerCase().trim();
// Your bot logic here
if (text === "/ping") {
sendMessage(chatId, "pong!");
}
return HtmlService.createHtmlOutput("OK");
} catch (error) {
return HtmlService.createHtmlOutput("Error");
}
}
function doGet(e) {
return HtmlService.createHtmlOutput("OK");
}
After Fixing
- Save your script (Ctrl+S)
- Deploy → Manage deployments → click the pencil icon
- Set Version to New version
- Click Deploy
- Clear stuck pending updates:
https://api.telegram.org/bot<TOKEN>/setWebhook?url=<URL>&drop_pending_updates=true
How to Check Webhook Info 🛠️
Via Browser
Open this URL in your browser (replace <TOKEN> with your bot token):
https://api.telegram.org/bot<TOKEN>/getWebhookInfo
Via GAS Function
Add this function to your script and run it:
function getWebhookInfo() {
const TELEGRAM_BOT_TOKEN = "YOUR_BOT_TOKEN";
const url = "https://api.telegram.org/bot" + TELEGRAM_BOT_TOKEN + "/getWebhookInfo";
const response = UrlFetchApp.fetch(url, { muteHttpExceptions: true });
const result = JSON.parse(response.getContentText());
Logger.log("URL: " + (result.result.url || "(not set)"));
Logger.log("Pending updates: " + result.result.pending_update_count);
Logger.log("Last error: " + (result.result.last_error_message || "(none)"));
}
What the Error Looks Like
When the 302 error occurs, your webhook info response will look like this:
{
"ok": true,
"result": {
"url": "https://script.google.com/macros/s/xxxxx/exec",
"has_custom_certificate": false,
"pending_update_count": 7,
"last_error_date": 1770214432,
"last_error_message": "Wrong response from the webhook: 302 Moved Temporarily",
"max_connections": 40,
"ip_address": "142.250.77.174"
}
}
Key indicators of the problem:
pending_update_countkeeps increasinglast_error_messageshows302 Moved Temporarily
After the Fix
Once you deploy with HtmlService.createHtmlOutput(), the response should look clean:
{
"ok": true,
"result": {
"url": "https://script.google.com/macros/s/xxxxx/exec",
"has_custom_certificate": false,
"pending_update_count": 0,
"max_connections": 40,
"ip_address": "142.250.77.174"
}
}
No last_error_message and pending_update_count stays at 0 — that’s how you know it’s working.
Now your Telegram bot should respond properly. Happy automating! 🤖