Web: site and web app
The web is one more platform of your app, beside iOS and Android. Ploa hosts it at a free address on ploa.app, and every version goes to testers, through Ploa's review and live, the way a build goes through the stores. A web app can have its own server side: an API, scheduled jobs and a database.
What to ask your agent
Add a showcase site for this app with Ploa, and send the first version to testers.
Build the web app and let dana@example.com test it. Release it tonight at 22:00 once I say it's good.
Connect www.example.com to the site and tell me the DNS records to add.
Make a server for this web app in Ploa's cloud, deploy the API, and run the database migrations on every release.
The tools behind these: Every MCP tool › The web and The app's server.
Who does what
| What it does | |
|---|---|
| Your agent | Asks you which address and which kind of web, adds it, builds each version with the project's own framework and sends it, sets who tests it, connects your domain, and makes and deploys the server side. |
| Ploa | Numbers each version, checks it on upload and again on release, puts it at its own testing address, hosts the site with its certificate, keeps the last 10 versions, and runs the server in Google Cloud. |
| You | Choose the address and what the web is, approve each release, add the DNS records for a domain you own, give the server's secrets, and choose where the server runs. |
What the web of an app is
You pick one of three, and can change it later:
| Kind | What it is |
|---|---|
| Showcase site | Pages about the app: what it does, screenshots, links to its store apps. |
| Web app | The app itself in the browser, built from the same project. |
| Both | The showcase pages at the address, and the web app under /app. |
- Installable (PWA) is a switch. On, people can add it to their home screen, and Ploa checks its manifest, icons and service worker in every version.
- An app in both stores doesn't need the PWA on phones: people should get the store apps. Leave it off, or turn it on for computers and point phones at the stores (Ploa's check says what's missing).
- An app can be on the web alone, with no store apps at all.
The address, and a domain you own
Every site gets a free address, <name>.ploa.app, such as myapp.ploa.app. You pick the name; your agent asks you rather than choosing it.
Ploa sells no domains. You can connect a domain you already own, on plans that include one (see Room by plan):
- Add the domain on the app's Web page, with
ploa web domain example.com, or through your agent. - Ploa gives you the DNS records to add where the domain's DNS is managed: a TXT record that proves it's yours, and the record that points it at Ploa (A records for a bare domain, a CNAME for a subdomain such as
www). For a bare domain, remove the other A and AAAA records on@. - If the domain's DNS is at Cloudflare, the domain's card has Set it up in Cloudflare: you sign in to Cloudflare, pick the account the domain is in, see every record Ploa will add, change or remove, and Ploa writes them for you, DNS only, then checks the domain. Ploa uses that sign-in only for this and gives it back to Cloudflare right after. Or add the records yourself, with Proxy status set to DNS only. Otherwise Ploa doesn't change your DNS, and your agent doesn't either unless you ask it to.
- Ploa checks the records by itself every few minutes, and says what's still missing. DNS changes usually show within minutes, sometimes hours. Once they're right, the domain serves the site with its own certificate, made automatically.
A domain is connected to one workspace at a time.
Any web framework
ploa web build builds the project with its own framework's command, then sends the folder that build made. It knows:
Expo (web), Next.js with output: "export", Vite, Astro, SvelteKit with adapter-static, Nuxt (nuxi generate), React Router in SPA mode, Angular, Gatsby, Create React App, Vue CLI, Eleventy, Docusaurus, VitePress, Hugo, Jekyll, MkDocs, Flutter (flutter build web), any project with a build script, and plain HTML that needs no build.
- Missing packages are installed first, with the package manager the project's lockfile names.
--no-buildsends what's built already.- A Flutter app needs its
web/folder: add it once withflutter create --platforms=web .
Hosting serves static files. A project set up to run a server (Next.js without output: "export", Astro's server output, a SvelteKit server adapter, React Router or Remix with SSR) is refused before it builds, with how to make it static. An API, jobs and a database live in the app's server: see The web app's server side.
Versions
Every upload is a version. Its number is the version in the project (app.json, package.json, or pubspec.yaml for Flutter), so it lines up with iOS and Android, plus a build number Ploa counts: 1.4.1 (13). --version sets another version.
An upload runs like a build: the app's Web page shows its steps as they happen (upload, number, Ploa's review, up at its testing address), with Stop… while it's going up. A stopped version never reaches testers. Files like .env, .git and node_modules are never sent. Ploa keeps the files of the last 10 versions.
From built to live
| Step | What happens |
|---|---|
| Built | The version is uploaded and checked. |
| Testers | It's up at its own testing address, <site>--<build>.ploa.app. |
| Ploa's review | Ploa checks it again when it's released. |
| Live | Visitors get it at the site's address and your domains. |
Testers
Each version's testing address is its own, and search engines never index it. You choose who can open it:
- Testers: people you add by email sign in with a code sent to them, once per device for 30 days. The people of your workspace open it too.
- Password: anyone with the address and the password.
- Anyone with the link.
Write what to test, and testers see it when they sign in.
Ploa's review
Ploa checks every version on upload, and again on release. Anything unsafe stops it; the rest are notes. It checks that the site opens and every file it asks for is there, that it holds no keys, .env or private files, that no form sends a password to another site, the site's rules, that it fits in the plan's room, that it reads well on a phone, a title and a share image, installability when the PWA switch is on, and links to the store apps.
Release and going back
- You release it yourself, as with App Review and production, and it takes the App Review and production permission. On the Web page, Release… asks what and when, then Yes, release. From the CLI or an agent, it asks you, and you approve it in Approvals on ploa.dev. An autonomous agent's release goes at once.
- Now or at a time within the next 30 days:
ploa web release 13 --at 2026-11-06T09:00. - Going back is at once, with no review, to any of the last 10 versions that was live: Go back to this on an earlier version, or
ploa web rollback.
Site rules (ploa.json)
A version can carry its own rules in ploa.json, in the built folder or beside the project's package.json. The CLI and your agent add it to the upload, and it stays out of your build, so a deploy elsewhere never serves it.
{
"rewrites": [{ "from": "/he/*", "to": "/he.html" }],
"redirects": [{ "from": "/old", "to": "/new", "status": 301 }],
"headers": [{ "path": "/*", "set": { "Content-Security-Policy": "default-src 'self'" } }],
"proxy": [{ "from": "/api/*", "to": "https://api.example.com", "strip": true }]
}- Paths are exact (
/he) or a prefix ending in/*. The first proxy, then redirect, then rewrite that fits a request decides it; every headers rule that fits adds its headers. Up to 50 of each. A redirect from a prefix to an address ending in/*keeps the rest of the path. - Headers a site may set: Content-Security-Policy (and its Report-Only), Cache-Control, Permissions-Policy, Referrer-Policy, the three Cross-Origin policies, X-Frame-Options, X-Robots-Tag, Access-Control-Allow-Origin, Link, Content-Type and Strict-Transport-Security (a year or more). Never Set-Cookie.
- A proxy sends a path to the site's own API over https, by name only, to a domain your workspace connected and proved. Cookies, the body and the answer pass through as they are, and the API hears the visitor's own address. WebSockets don't pass yet.
/.well-known/apple-app-site-associationandassetlinks.json, when the site has them, are served as JSON, as Apple and Google read them.
The web app's server side
A web app can have its own server: an API, scheduled jobs and a Postgres database, run on Google Cloud and driven through Ploa. It's part of the app's web, not something you buy: there are no server packages to pick.
| In Ploa's cloud | In your own Google Cloud | |
|---|---|---|
| Plans | Pro and Team, one for each app, with the room the plan gives | Every plan, Free included |
| Who pays Google | Ploa; it's in the plan | You, directly |
| Where | Google Cloud in Tel Aviv | Your project, in the region you pick |
| Database | Your own database and role on a database server Ploa shares between servers | Your own Cloud SQL instance (Postgres 16, the smallest size to start, backups on), which you can grow in Google Cloud |
| Setting it up | Nothing: it's made at once | Ploa prints a few commands you run once in Cloud Shell to let it in, then it carries on |
The room each app's server has in Ploa's cloud:
| Pro | Team | |
|---|---|---|
| The API | 0.25 vCPU and 512 MB, sleeps when idle (the first request after a quiet spell waits for it to start) | 0.25 vCPU and 512 MB, always ready |
| Postgres | 512 MB, 5 connections | 1 GB, 10 connections |
| Traffic out a month | 2 GB | 5 GB |
| Scheduled jobs | 1 | 3 |
- Any language. With a
Dockerfilein the server's folder, Ploa builds that. Otherwise it builds Node, Python, Go, Java, Ruby, PHP and .NET as they are (astartscript, aProcfile…). The server listens on thePORTvariable. - Deploying sends the server's folder from your project, or has Ploa take it from the app's linked GitHub repository (a commit, or the branch's head). In a git checkout only the files git keeps are sent; elsewhere the folder without dependencies,
.envfiles and keys. Then the release command runs, if you set one (database migrations, say:npx prisma migrate deploy), and the new version goes live. If it fails, the version doesn't. The one before stays for going back. - Variables: plain ones show; secrets (keys, passwords) are kept in Ploa's vault and Google's Secret Manager and never shown again.
DATABASE_URLis set by Ploa. Setting or removing one restarts the live version. Your agent never sets a secret you didn't give it. - Scheduled jobs run a command with the live version, on cron's five fields in UTC, at most every 5 minutes.
- Logs show what the server and its jobs printed, requests and crashes, newest first.
- A command once (a seed script, a one-off fix) runs with the live version, its variables and database. There's no SSH: a server is containers that come and go, and a change made by hand inside one is gone with it.
- The SQL console runs as the server's own role, so it reaches that database only. It's read-only unless you ask to write.
- Stopping a server closes it to everyone and keeps its database. Deleting is an owner's, in the browser, typing the app's name: in Ploa's cloud its database goes too; in your own project, the database instance goes only if you tick it.
An agent creates, deploys, and sets variables and jobs with the draft level. Running a command, writing to the database, stopping and going back need the production level. See Access and approvals.
On the dashboard and from the terminal
On the dashboard, each app's App settings › Web page has what the web is, the PWA switch, a zip to upload, versions with their steps, testers, release, going back and domains, and under it the web app's server side: where it stands, its versions, variables, jobs, logs and the SQL console. Settings › Hosting shows how much room the plan has, what your sites use, every site and every domain you connected.
ploa web build # build, send a new version to testers ploa web status # sites, addresses, versions ploa web release # the newest tested version, live ploa web rollback # back to the version live before ploa web domain example.com # connect a domain you own ploa server # the app's server: where it stands ploa server create --ploa # in Ploa's cloud (Pro, Team); or --own <project> ploa server deploy # the server's folder; --github from the repository ploa server env KEY=value --secret # a secret variable ploa server logs ploa server jobs --name cleanup --schedule "0 3 * * *" --command "node cleanup.js" ploa server run node seed.js # a command once ploa server sql "select count(*) from users"
Your agent stops a version that's still going up with ploa_release_stop. Every tool for the web and the server: Every MCP tool › The web.
Room by plan
Every plan comes with its own hosting; plans differ in how much.
| Free | Starter | Pro | Team | |
|---|---|---|---|---|
| Websites | 1, the app's | 1 | One per app | One per app |
| Storage | 100 MB | 5 GB | 10 GB | 50 GB |
| Traffic a month | 1 GB | 50 GB | 100 GB | 500 GB |
| Domains you own, connected | 0 | 1 | 3 | Unlimited |
| The web app's server side | In your own Google Cloud | In your own Google Cloud | One per app in Ploa's cloud, or your own | One per app in Ploa's cloud, with more room, or your own |
| "Made with Ploa" badge | Yes | No | No | No |
On Pro and Team, each app past the plan's count adds its share (3 GB and 30 GB on Pro, 2.5 GB and 25 GB on Team). A version that doesn't fit the storage is refused, unless an owner turned on extra usage. Plans and prices: Workspace, team and plans.
Not yet
- Ploa adding the files that make links open the store app (
apple-app-site-association,assetlinks.json) and a smart app banner. You can add those files yourself. - A new web version on every GitHub push.
- Releasing to a share of visitors at a time.
- Your own domain for the server, a live shell, and Redis or MongoDB. A server's traffic out isn't counted against its room yet.