Fix HTML form POST to Google Apps Script Web App (2025)
Fix HTML form POST failures to Google Apps Script web apps after June 2025. Redeploy the web app, reauthorize scopes, update /exec URL, and avoid CORS preflight.
HTML form posting to a Google Apps Script web app URL (action=“https://script.google.com/macros/s/AKfycbyzSs8KWgP6lgtOS83j9LLxJmQ5S8qmOrURFPUM/exec”) stopped working since June 2025 — how can I fix it?
I have used the following HTML form on my personal site since 2014. It worked until June 2025 but now submissions no longer reach the Apps Script web app. What changed in Apps Script deployment/authentication and what steps should I take to redeploy or update the form so POST submissions work again?
Form markup:
<form class="gform pure-form pure-form-stacked" method="POST" data-email="toto@toto.fr" action="https://script.google.com/macros/s/AKfycbyzSs8KWgP6lgtOS83j9LLxJmQ5S8qmOrURFPUM/exec">
<div class="form-elements">
<fieldset class="pure-group">
<label for="name">Vos nom et prénom: </label>
<input id="name" name="name" placeholder="nom et prenom" />
</fieldset>
<fieldset class="pure-group">
<label for="message">Message: </label>
<textarea id="message" name="message" rows="10" placeholder="votre message"></textarea>
</fieldset>
<fieldset class="pure-group">
<label for="email">Votre adresse Email:</label>
<input id="email" name="email" type="email" value="" required placeholder="name@email.fr"/>
<span class="email-invalid" style="display:none">Veuillez mettre une adresse valide</span>
</fieldset>
<!-- <fieldset class="pure-group">
<label for="color">Favourite Color: </label>
<input id="color" name="color" placeholder="green" />
</fieldset> -->
<button class="button-success pure-button button-xlarge"> <i class="fa fa-paper-plane"></i> ENVOI</button>
</div>
<!-- Customise the Thankyou Message People See when they submit the form: -->
<div class="thankyou_message" style="display:none;">
<h2><em>Merci</em> de m'avoir contacter Je vous répondrais bientôt !</h2>
</div>
</form>
Any suggestions for troubleshooting (redeploy options, access settings, CORS/authorization changes, or other fixes) to restore form submissions?
Your HTML form stopped reaching the Google Apps Script web app because Apps Script changed deployments, OAuth/granular consent, and runtime behavior in 2025 — old deployment IDs, missing re‑authorization, or access settings (anonymous posts) are the usual culprits. Redeploy the web app (Deploy → New deployment → Web app), set Execute as → Me and Who has access → Anyone, even anonymous, copy the new /exec URL into your form action, re‑authorize required scopes, and test with curl or the Executions log; if you use AJAX, avoid preflight (send text/plain or application/x-www-form-urlencoded) or implement doOptions/doPost parsing.
Contents
- What changed in Google Apps Script (June 2025)
- Quick‑check checklist (fast path)
- Step‑by‑step: redeploy your Apps Script web app
- Authorization, scopes and consent — reauthorize & verify
- CORS, AJAX and form submission tips for Apps Script web apps
- Diagnosing errors: curl, Executions log, and common HTTP codes
- Advanced fixes & when it’s a platform regression
- Sample doPost / doOptions code and form examples
- Sources
- Conclusion
What changed in Google Apps Script (June 2025)
Short version: several platform and policy changes in 2024–2025 made older public web‑app setups fragile. In particular:
- Granular OAuth / consent changes rolled out starting January 2025 that change how scripts request and receive scopes from users and the IDE; scripts that need new scopes may require re‑authorization or verification (Workspace updates blog).
- Deployment and runtime behavior evolution: the deployment model requires creating and managing explicit deployments and a redeploy is needed when the project changes; old deployment IDs can become invalid if a deployment was removed or recreated (Deployments doc, release notes).
- Runtime changes (Rhino → V8) and other infra updates were announced; while runtime migration alone usually doesn’t drop POSTs, it can require small code changes and re‑testing (release notes).
- Finally, community reports in mid‑2025 described platform regressions where POSTs returned 500 or 405 and doPost never ran — hinting at server‑side handling changes that can break older client flows (Google Groups report, Developer forum thread).
So: if your site has been posting to the same /exec URL since 2014, either the deployment behind that URL was changed/deleted, your script now needs scopes/verification, or browser/request behavior (preflight/CORS) is interfering.
Quick‑check checklist (fast path)
Try these checks in order — they fix most breakages in minutes:
-
- Test the endpoint directly with curl or Postman (see Diagnose section).
-
- Open the Apps Script project → Deploy → Manage deployments. If no active web app deployment or the ID changed, redeploy (next section) and copy the new /exec URL (deployments doc).
-
- Set Execute as → Me and Who has access → Anyone, even anonymous if the form is public (then update your form action).
-
- Run any function from the script editor to force the authorization prompt; accept scopes. If your code uses Gmail/Sheets/Drive, reauthorize (authorization doc).
-
- If your front end uses fetch/XHR, confirm you’re not triggering a preflight (see CORS section). If you are, send a “simple” request or implement doOptions/doPost handling.
-
- Check the Executions/Logs for errors after a test submit (troubleshooting doc).
If step 1 returns 405 / 500 or the request never hits doPost, redeploy and re‑test.
Step‑by‑step: redeploy your Apps Script web app
Follow these exact steps in the Apps Script editor (new IDE):
- Open the script project.
- Click Deploy → Manage deployments → New deployment.
- For Deployment type choose “Web app”.
- Set “Execute as” → Me (runs with your account’s permissions).
- Set “Who has access” → Anyone, even anonymous (if you want public form POSTs).
- Click Deploy and copy the Web app URL (it ends with /exec).
- Update your HTML form action to that new URL.
- Test the form submission.
Why those settings? Running as your account lets the script call services (MailApp, Sheets) using your permissions; setting access to “Anyone, even anonymous” allows a browser POST from a visitor without a Google sign‑in. See the official steps in the deployments guide for screenshots and details: Create and manage deployments. If you edit code later, create a new deployment and update the form again — deployments are versioned, and old IDs can become stale.
Authorization, scopes and consent — reauthorize & verify
If the script uses services that access private data (Sheets, Gmail, Drive, advanced APIs), the platform may now require explicit scopes and re‑consent. Actions:
- In the script editor, run a simple function (or open Run → Run function) to trigger a consent flow and re‑grant scopes. This forces OAuth prompts so the owner can reauthorize.
- If you see a “This app isn’t verified” warning and you use sensitive scopes, you may need to configure the OAuth consent screen in the Google Cloud project and (for broad public use) submit for verification; see the troubleshooting auth doc: Troubleshoot authentication & authorization issues.
- If you prefer not to expose the script publicly, consider moving the form submission to your server (proxy) and forwarding data to the Apps Script using your own credentials.
A quick test: if a manual run from the editor succeeds but web POSTs fail, that points to a permission or access setting rather than code logic.
CORS, AJAX and form submission tips for Apps Script web apps
Important distinction: a plain HTML form POST (method=“POST”, no JavaScript) is a “simple” browser navigation and usually doesn’t trigger a CORS preflight. But if you submit via fetch() or XHR, or set custom headers/content‑types, the browser will send an OPTIONS preflight and the server must respond appropriately.
Options:
- Keep it simple: submit the form as a regular POST (no fetch). That avoids preflight and usually lands in doPost(e).
- If you must use fetch/XHR, send a “simple” request to avoid preflight. Example fetch that avoids preflight:
fetch('https://script.google.com/macros/s/DEPLOY_ID/exec', {
method: 'POST',
headers: { 'Content-Type': 'text/plain;charset=UTF-8' },
body: JSON.stringify({ name, email, message })
});
On the Apps Script side parse JSON with:
function doPost(e) {
var data = JSON.parse(e.postData.contents);
Logger.log(data);
return ContentService.createTextOutput(JSON.stringify({status: 'ok'}))
.setMimeType(ContentService.MimeType.JSON);
}
- If you can’t avoid preflight, implement doOptions(e) to handle the OPTIONS request. Note: Apps Script has limited ways to set arbitrary response headers, and many community posts recommend avoiding preflight where possible. See practical guidance on CORS in community answers and how to avoid preflight: StackOverflow CORS thread, IITH blog on CORS, and a pragmatic workaround (send text/plain) described in community posts (Medium example).
If you see console errors like “No ‘Access-Control-Allow-Origin’ header is present”, that usually means your AJAX request triggered preflight or the response did not include the header. For many static sites the fastest fix is to avoid AJAX or to send a simple request.
Diagnosing errors: curl, Executions log, and common HTTP codes
Start with a direct POST from your machine:
- Test with curl (replace DEPLOY_ID):
curl -v -X POST -d "name=Jean&email=jean@example.com&message=Hello" \
"https://script.google.com/macros/s/DEPLOY_ID/exec"
- HTTP 200 — good. Check response body.
- HTTP 405 — Method Not Allowed (server not accepting POST at that URL; usually wrong endpoint or platform redirect behavior).
- HTTP 500 — server runtime error; open the Apps Script Executions panel to see the exception trace.
To view server-side logs: open the Apps Script project → left menu “Executions” (or View → Executions in older UI) and click the failing run to see stack traces and logged data. Add diagnostic logging:
function doPost(e) {
Logger.log(JSON.stringify(e)); // inspect parameters and postData
// ... your processing ...
}
The official troubleshooting page walks through common failure modes: Troubleshooting | Apps Script.
Advanced fixes & when it’s a platform regression
If you’ve redeployed, reauthorized, avoided preflight and the POST still never reaches doPost (or you get consistent 500/405 responses), this could be a platform regression — several developers reported this around June 2025. Actions:
- Search the public thread reports (example: Google Groups report, Developer forum thread).
- If you confirm widespread reports, either: (a) file feedback/issue from the Apps Script editor, (b) post to the Google Apps Script community, or © use a small server proxy (on your domain) to receive the form and forward to the Apps Script (this avoids relying on the script endpoint during outages).
- If you have Google Workspace support (paid), open a support ticket with the execution logs and curl output.
Community posts and GitHub issues document real cases where POST data was lost across redirects or script.googleusercontent.com endpoints mishandled POST — keep that in mind if logs show no incoming execution even though curl gets a 200/500.
Sample doPost / doOptions code and form examples
Minimal doPost for a regular form (application/x-www-form-urlencoded):
function doPost(e) {
Logger.log(JSON.stringify(e)); // helpful when debugging
var name = e.parameter.name || '';
var email = e.parameter.email || '';
var message = e.parameter.message || '';
// e.g., append to a Sheet or send an email
// return a simple confirmation page
return HtmlService.createHtmlOutput('<p>Thanks — received.</p>');
}
If you accept JSON via text/plain (AJAX workaround):
function doPost(e) {
var body = e.postData && e.postData.contents ? e.postData.contents : '{}';
var data = JSON.parse(body);
Logger.log(data);
return ContentService.createTextOutput(JSON.stringify({ok: true}))
.setMimeType(ContentService.MimeType.JSON);
}
Simple doOptions stub (attempt to handle preflight — behaviour can vary):
function doOptions(e) {
// Apps Script has limited control over headers; success depends on platform support
return ContentService.createTextOutput('')
.setMimeType(ContentService.MimeType.TEXT);
}
Finally, update your form action to the new URL you copied after redeploy:
<form method="POST" action="https://script.google.com/macros/s/NEW_DEPLOY_ID/exec">
<!-- your fields -->
</form>
Sources
- Google Apps Script release notes | Google for Developers
- Troubleshooting | Apps Script | Google for Developers
- Create and manage deployments | Apps Script | Google for Developers
- Authorization for Google Services | Apps Script | Google for Developers
- Troubleshoot authentication & authorization issues | Apps Script | Google for Developers
- Web Apps | Apps Script | Google for Developers
- Granular OAuth consent in Google Apps Script IDE executions (Workspace Updates)
- Google Groups: Web App doPost returns 405 / e undefined (community report)
- Apps Script Web App POST requests fail with 500 (Google Developer forums)
- How do I allow CORS requests in my Google Script? (StackOverflow)
- Getting ‘CORS policy’ error in console when I submit my form (GitHub issue)
- Fixing CORS Errors in Google Apps Script | IITH blog
- Struggling with CORS in Google Apps Script? Here’s the fix (Medium)
- Deploy as Webapp - new version every time? (StackOverflow)
- AppsScriptPulse – aggregator of Apps Script updates
Conclusion
Brief recap: your form stopped reaching the Google Apps Script web app because of deployment, OAuth/consent, and runtime changes (plus possible mid‑2025 platform regressions). Redeploy the web app, set Execute as → Me and Who has access → Anyone, even anonymous, update the form action to the new /exec URL, reauthorize required scopes, and test with curl and the Executions log. If you use AJAX, either send a simple request (text/plain or application/x-www-form-urlencoded) to avoid preflight or handle OPTIONS/remove custom headers; if all else fails, check community reports and file an issue.