Copyright © 2026 Alex Mirici — Web Developer.
WebToApp turns any website (URL) or static project (HTML/CSS/JS ZIP) into an installable Android APK. Paste a link, wait a few minutes, download the app.
The site runs inside a native WebView, so it works even for sites that block <iframe> embedding. The APK is compiled on GitHub Actions — free and unlimited for public repositories — so no Android SDK, no local toolchain and no paid build service is needed.
Visitors never enter a token: the GitHub credentials live only on the server, configured once by the site owner.
| 🔗 URL mode | Wrap any live website into a native app. |
| 📦 ZIP mode | Package a static site (HTML/CSS/JS) as an offline-capable app. |
| ⚡ One-click build | The visitor only pastes a link or drops a ZIP. |
| 👀 Smart preview | Interactive <iframe> when the site allows it, automatic screenshot when it sends X-Frame-Options: DENY / frame-ancestors 'none'. |
| 📱 QR install | Scan a QR code and download the APK straight to the phone, from any device. |
| 🏷️ Version & build number | Set versionName and versionCode from the dashboard (1.2.3, 42, …). |
| 🕵️ Custom User-Agent | Append your own UA string to the WebView (applied natively via Capacitor). |
| 🎨 Icons & splash | Android densities generated automatically from a single 1024×1024 artwork. |
| 📲 Installable PWA | The dashboard itself can be installed as an app. |
| ☁️ Signed release | Release APK and AAB when signing secrets are configured, debug fallback otherwise. |
| 🔐 Server-side token | No credentials in the browser, no secrets in the repo. |
| 🛡️ Anti-abuse | Max 4 concurrent builds and 40 builds/hour protect the Actions quota. Beyond that, requests are queued and processed automatically when resources become available. |
Visitor (browser)
│ pastes a URL / drops a ZIP
▼
Dashboard (Next.js on Netlify or Vercel)
│ dispatches a workflow with a server-side GitHub token
▼
GitHub Actions ── Capacitor project + Android build (Gradle)
│
▼
Artifact (.apk / .aab) ── streamed back by the dashboard, also via QR code
Se la quota Actions è esaurita, la richiesta viene messa in coda ed elaborata automaticamente non appena diventano disponibili risorse (build simultanee o quota oraria). Il frontend può monitorare lo stato tramite /api/cloud-latest.
The dashboard host does not compile anything: it only serves the UI and triggers the workflow. Compilation always happens on GitHub Actions.
- Open webstoapp.netlify.app.
- Paste a URL or drop a ZIP with your site.
- (Optional) App Settings → name, package ID, version, build number, User-Agent, icon.
- Press Start Build.
- When the build is green you land on Download: grab the APK or scan the QR code with your phone.
Typing
example.comis enough — the URL is normalized tohttps://example.com.
🆔 Package ID is what makes Android see two apps as different. The dashboard generates one from your site (
com.webtoapp.<site>); if two builds share the same ID, the second is installed as an update of the first and replaces it. Change it before building a different app, and keep it stable to ship updates of the same app.
Create a classic Personal Access Token with the repo and workflow scopes, then set two environment variables on your host:
| Variable | Value |
|---|---|
WEBTOAPP_GITHUB_TOKEN |
Your GitHub PAT — secret, server-only |
WEBTOAPP_GITHUB_REPO |
owner/repo, e.g. jeaders/WebToApp |
Where to set them:
- Vercel: Project → Settings → Environment Variables
- Netlify: Site settings → Build & deploy → Environment → Environment variables (scope All scopes)
Redeploy and open /api/config: it must return {"configured":true,...}. From then on the token fields disappear from the UI and visitors only paste a link.
🔁 Anti-abuse: any visitor can start a build on your account, so the service enforces 4 concurrent builds and 40 builds/hour. Beyond that, requests are queued and processed automatically when GitHub Actions capacity becomes available, then the build resumes.
🧪 For local development copy
.env.exampleto.env(git-ignored) and fill the same two variables.
Requirements: Node.js 18+ (20/22 recommended). No Android SDK needed.
npm install
npm run dev
# http://localhost:3000Production build:
npm run build && npm startAPK compilation is independent of the dashboard host. The dashboard can run anywhere that supports Next.js API routes:
Vercel — recommended (free Hobby plan, no badge)
- vercel.com → Add New → Project → import the repo.
- Framework: Next.js (auto-detected).
- Add
WEBTOAPP_GITHUB_TOKENandWEBTOAPP_GITHUB_REPO. - Deploy — every push to
mainredeploys automatically.
Netlify
- Connect the repo; each push to
maindeploys automatically. ⚠️ On the free plan, when credits run out the site is paused until the monthly reset. The APK build is not affected: you can still run the workflow from GitHub → Actions → Build Mobile Apps → Run workflow.- ℹ️ The free plan may inject a "Powered by Netlify" badge on the page.
Serverless notes
- ✅ Cloud builds (URL and ZIP) work on both hosts.
⚠️ The local "source ZIP" generator is disabled on serverless (read-only filesystem); only the cloud build is available.- ⚡ API routes answer immediately, so host function timeouts are never hit.
The workflow builds a signed release APK (and an AAB, the format required by Google Play) when these GitHub Actions secrets are present:
| Secret | Value |
|---|---|
ANDROID_KEYSTORE_BASE64 |
base64 of your .jks / .keystore file |
ANDROID_KEY_ALIAS |
key alias (e.g. webtoapp) |
ANDROID_KEYSTORE_PASSWORD |
keystore password |
ANDROID_KEY_PASSWORD |
key password |
Without them the workflow safely falls back to a debug build, so the pipeline never breaks.
Icons & splash: the artwork lives in assets/icon.png (1024×1024) and assets/splash.png (2732×2732); @capacitor/assets renders every Android density at build time.
🔐 Keep the keystore and its passwords in a safe place — never in the repository. The same key is required to publish updates to an app already on the Play Store.
ℹ️ Install warning: a properly signed APK removes the "unknown app" warning caused by broken signatures. Google Play Protect may still show a generic notice for brand-new sideloaded apps until they gain reputation — that is normal outside the Play Store.
- The GitHub token lives only on the server (environment variable) and is never sent to the browser.
- No token or secret is committed:
.env*,*.local.md,*.local.jks,*.keystoreand generated folders are git-ignored. - The preview endpoint (
/api/embed-check) refuses private/loopback addresses (SSRF protection) and only acceptshttp/https. - If a token is ever exposed, revoke it immediately in GitHub → Settings → Developer settings.
.github/workflows/build.yml # Android APK/AAB build (self-contained)
.github/workflows/netlify-deploy.yml # Optional Netlify build hook
.github/scripts/customize-android.js # versionName / versionCode / appendUserAgent
src/app/page.tsx # Dashboard UI (upload, preview, settings, logs, download)
src/app/layout.tsx # Metadata, PWA manifest, Search Console verification
src/components/PwaRegister.tsx # Service worker registration + install button
src/app/api/github-build/route.ts # Starts the cloud build (URL or ZIP)
src/app/api/cloud-latest/route.ts # Finds / follows the workflow run
src/app/api/cloud-status/route.ts # Run status
src/app/api/cloud-artifact/route.ts # Streams the APK (POST download + GET for QR)
src/app/api/embed-check/route.ts # X-Frame-Options / CSP detection (SSRF-guarded)
src/app/api/config/route.ts # Reports whether the service is configured
src/app/api/build/route.ts # Local source builder (development only)
src/lib/github.ts # GitHub API helpers + server config
src/lib/i18n.ts # English / Italian strings
public/manifest.json, public/sw.js # PWA manifest and service worker
docs/hero.png, docs/download.png # README artwork (dashboard, Download + QR)
docs/social-preview.png # GitHub social preview image (1280×640)
| Problem | Fix |
|---|---|
| "Build cloud not configured" | Set WEBTOAPP_GITHUB_TOKEN and WEBTOAPP_GITHUB_REPO, then redeploy. |
| App shows only "Loading…" | Should not happen — the workflow verifies the native config and fails loudly. Rebuild the APK. |
| ZIP build fails | The archive must contain an index.html (at the root or inside a single top-level folder) and stay under ~45 MB. |
| Old APK crashes on launch | Regenerate it: builds made before URL normalization (https://) could crash on some devices. |
| Preview is a static screenshot | The site blocks embedding (X-Frame-Options / CSP). The screenshot is intentional; use Open in a new tab for the real thing. |
| QR points to localhost | You generated it from npm run dev. On the deployed site the QR always uses the public domain. |
| A new app replaces the previous one | Both share the same Package ID (App Settings): Android treats them as the same app. Give each app its own ID — the dashboard does it automatically from the site URL, placeholders like com.yourname.app are rejected. |
Released under the MIT License — see LICENSE.
Copyright © 2026 Alex Mirici — Web Developer.
Third-party open-source dependencies (Next.js, React, Capacitor, Tailwind CSS, Octokit, AdmZip, qrcode, …) remain governed by their own licenses.

