Deploy Apps Script as a public web app
Serve a page anyone can visit — Northwind's public status page or microsite from one script.
Published Jun 25, 2025
Northwind needs a public page that lives outside the marketing site — a status board during an outage, a small microsite for an event, a one-off landing page for a partner. Spinning up a server, a domain, and a deploy pipeline for that is overkill. Apps Script can serve HTML at a stable Google URL, with no hosting bill and no infrastructure to mind.
This script is the smallest possible public web app — a single doGet that
returns a page. Once you understand the deploy steps, the same pattern carries
the bigger web-apps in this hub: quizzes, registration forms, webhook receivers.
Get this one live first; the rest is just more HTML.
What you’ll need
- A Google account that can create Apps Script projects.
- Two minutes for the first deploy (most of it is clicking through the authorisation prompt).
- Nothing else — no Sheets, no API keys, no external services.
The script
// The page title shown in the browser tab. Pull values like this out of
// the HTML so the markup stays clean and the config sits in one place.
const PAGE_TITLE = 'Northwind Studios';
// The body markup. For anything longer than a paragraph, switch to
// HtmlService.createHtmlOutputFromFile and put it in a .html file.
const PAGE_BODY =
'<h1>Northwind Studios</h1>' +
'<p>We make things. Come back soon for the new site.</p>';
/**
* Entry point for the public web app. Apps Script calls doGet whenever
* someone visits the deployment URL — the return value is the response.
*
* @param {GoogleAppsScript.Events.DoGet} _e Query parameters, ignored here.
* @return {GoogleAppsScript.HTML.HtmlOutput} The page to serve.
*/
function doGet(_e) {
// HtmlOutput is Apps Script's response object for HTML. setTitle controls
// the browser tab; the body is whatever string you pass to createHtmlOutput.
return HtmlService.createHtmlOutput(PAGE_BODY)
.setTitle(PAGE_TITLE)
.addMetaTag('viewport', 'width=device-width, initial-scale=1');
}
How it works
doGetis the magic name. When the web app is deployed and someone visits the/execURL, Apps Script invokes this function and returns whatever it produces.HtmlService.createHtmlOutputwraps a string of HTML in anHtmlOutputobject — the type Apps Script knows how to send as a response.setTitlesets the<title>element shown in the browser tab; the meta viewport tag makes the page render sensibly on phones.- The
_eparameter holds query-string values like?name=ada. We ignore it here because this page does not vary. The webhook and quiz articles in this hub show what to do with it.
Example run
Deploy the script and visit the /exec URL. The browser shows:
| Element | Value |
|---|---|
| Tab title | Northwind Studios |
| Heading | Northwind Studios |
| Paragraph | We make things. Come back soon for the new site. |
| URL shape | https://script.google.com/macros/s/AKfy.../exec |
The URL is long but stable. Bookmark it, share it, redirect a friendly DNS name to it — it stays the same across re-deploys.
Deploy it
- In the Apps Script editor, click Deploy → New deployment.
- Click the gear icon and choose Web app as the type.
- Set Execute as to Me so the script runs under your account.
- Set Who has access to Anyone for a fully public page, or Anyone with a Google account if you want a sign-in wall.
- Click Deploy, approve the authorisation prompt, and copy the Web app
URL ending in
/exec. That is your public address. - For development, use the Deploy → Test deployments dialog and copy
the
/devURL — it always serves the head version, so you can refresh after every edit without re-deploying.
Watch out for
- The
/execURL serves the deployed version, not the head version. Edits in the editor do not appear publicly until you run Deploy → Manage deployments → pencil icon → New version. The/devURL is the opposite: always head, always private to editors. - Anyone-access still goes through
script.google.com. That is fine for most uses; if you need a clean custom domain, put a redirect or reverse proxy in front of it. - HTML strings inside
.jsfiles get unwieldy fast. For more than a few lines, move the markup into a.htmlfile and useHtmlService.createHtmlOutputFromFile. The quiz and registration articles in this hub take that approach. - Web apps cannot read request headers or cookies the way a normal server can. If you need session state, store it keyed by a token in Script Properties or a Sheet — the expiring-download article shows the pattern.
- The first deploy asks for a long list of scopes — granting them is a one-off. Subsequent re-deploys only re-prompt if you have changed which services the script touches (added Gmail, added Drive). If a re-prompt appears unexpectedly, check the imports — something has been pulled in by accident.
- The Apps Script editor distinguishes between head version (whatever you
see in the editor) and deployed version (whatever was live as of the last
deploy). When debugging, always confirm which one you are testing. Visiting
the
/execURL but expecting head-version behaviour is the most common cause of “my change isn’t showing up”. - Once you have a real release, see Version and roll back a web app safely before you publish a second version on top of the first.
Related
Build a branded approval interface
Approve Northwind requests through a custom UI — clients click, decision is logged.
Updated Nov 8, 2025
Build an interactive quiz or assessment app
Run Northwind tests with scoring and feedback — questions in a Sheet, results in another.
Updated Nov 4, 2025
Build a multi-page web app with routing
Structure a real Northwind app across views — query-param routing, shared layout.
Updated Oct 31, 2025
Build a form-to-PDF web service
Convert Northwind form submissions to PDFs on the fly — POST in, PDF out.
Updated Oct 27, 2025
Build an expiring secure-download generator
Issue time-limited Northwind links via a web app — token in URL, server-side check.
Updated Oct 23, 2025