Como publicar seu app sem as lojas de aplicativos, usando a Back4app

Seu app pode estar na tela inicial de alguém hoje à tarde, com ícone e sem barra de endereço, e ninguém precisa aprovar. Três arquivos fazem isso — e um deles é o que todo mundo deixa de fora.

Uma linha do tempo em escala comparando dois caminhos até a mesma tela inicial de celular: o caminho da loja gasta 14 dias num teste fechado com 12 testadores e depois até sete dias de revisão, chegando ao celular no dia 21 e só se for aprovado, enquanto o caminho da web chega lá no dia 0, assim que três arquivos são servidos por HTTPS

Tradução do original em inglês, testado em 27 de outubro de 2026. Código, números e datas são os do original. Read in English

Você terminou o app. Agora quer colocá-lo num celular, e as duas portas da frente têm fila.

O Google Play pede a contas pessoais de desenvolvedor um teste fechado: pelo menos 12 testadores, participando sem interrupção por 14 dias, antes mesmo de você poder solicitar acesso à produção. A análise dessa solicitação, depois, “normalmente leva sete dias ou menos”.

A Apple faz uma pergunta diferente, e a diretriz 4.2 a coloca sem rodeios: seu app “deve incluir recursos, conteúdo e interface que o elevem além de um site reempacotado”.

Existe uma terceira porta, e ela não tem fila nenhuma. Um web app pode ficar na tela inicial com ícone próprio, abrir em tela cheia sem barra de endereço e continuar funcionando sem conexão. Este guia constrói um, e depois prova as duas metades dessa afirmação contra um navegador de verdade.

Prefere assistir?

As lojas de aplicativos são o número 13 da nossa contagem regressiva do que realmente dá errado para quem constrói com IA.

Video: We Analyzed 130,000 Vibe Coder Posts. These Are the Problems That Actually Hurt.
Watch it instead We Analyzed 130,000 Vibe Coder Posts. These Are the Problems That Actually Hurt.
Do canal da Back4app. Este guia é a versão longa da primeira posição do vídeo; as outras doze estão no post companion. Open on YouTube →

O que as lojas estão pedindo, de fato?

Duas coisas diferentes, e é por isso que o mesmo app pode travar nas duas por motivos sem relação entre si.

A do Google é aritmética. Desenvolvedores com contas pessoais criadas depois de 13 de novembro de 2023 precisam rodar um teste fechado com “no mínimo 12 testadores que tenham participado continuamente por pelo menos 14 dias”. Quem sai do teste zera o relógio daquela pessoa, então os doze têm que ficar. Contas de organização pulam o teste por completo.

A da Apple é editorial. A diretriz 4.2.2 acrescenta que apps “não devem ser principalmente material de marketing, anúncios, recortes da web, agregadores de conteúdo ou uma coleção de links”. Um site embrulhado numa casca nativa é exatamente o caso que a frase descreve, então o revisor que a lê já sabe o que procurar.

Antes que alguém possa instalar
12testadores, por 14 dias
→
7dias de revisão, normalmente
US$ 99por ano, na outra loja
Os números do Google vêm da documentação do próprio Play Console. A Apple cobra o programa de desenvolvedor anualmente, com o app aceito ou não.

O caminho da web não pede nada disso. Também não pede dinheiro, conta nem processo de recurso, porque não há a quem recorrer.

O que torna uma página web instalável?

Um arquivo de manifest. É isso mesmo, o mecanismo inteiro: um pequeno documento JSON que diz ao navegador como o app se chama, qual ícone usar e que ele deve abrir na própria janela.

{
  "name": "Shift Notes",
  "short_name": "Shift Notes",
  "start_url": "./index.html",
  "scope": "./",
  "display": "standalone",
  "background_color": "#F4F4EF",
  "theme_color": "#0A1220",
  "icons": [
    { "src": "icons/icon-192.png", "sizes": "192x192", "type": "image/png", "purpose": "any" },
    { "src": "icons/icon-512.png", "sizes": "512x512", "type": "image/png", "purpose": "any" },
    { "src": "icons/icon-maskable-512.png", "sizes": "512x512", "type": "image/png", "purpose": "maskable" }
  ]
}

Referencie-o na página e essa parte está pronta:

<link rel="manifest" href="manifest.webmanifest" />

display: standalone é a linha que remove a barra de endereço. Sem ela você tem um favorito; com ela você tem algo que uma pessoa vai descrever como um app, porque a coisa na tela não tem navegador nenhum dentro.

O que o navegador exige de verdade?

Menos do que os checklists dizem. Perguntamos ao próprio Chrome, uma mudança por vez, usando o veredito de instalabilidade dele em vez do resumo de um blog.

O que mudamosVeredito do Chrome
ícones em 192 e 512instalável
só um ícone 192instalável
só um ícone 512instalável
nenhum íconemanifest-missing-suitable-icon, no-acceptable-icon
display: browsermanifest-display-not-supported
sem short_nameinstalável

Então um ícone adequado basta, e o tão repetido “você precisa de 192 e 512” é um conselho sobre como o ícone aparece em telas diferentes, não uma condição para instalar.

Mais uma coisa que vale saber antes de começar a corrigir. Com três falhas presentes ao mesmo tempo — sem short_name, sem ícone grande e display: browser — o Chrome apontou exatamente uma: manifest-display-not-supported. Ele reporta o bloqueio em que esbarrou, não uma auditoria. Corrija e o próximo aparece.

Como medimos: cada linha é uma edição no manifest, depois um perfil novo do Chrome controlado pelo protocolo do DevTools, lendo Page.getInstallabilityErrors na página carregada. O script está no repositório.

Como fazer o app funcionar sem conexão?

Com um service worker, que é um script que o navegador mantém mesmo com a sua página fechada. Na instalação ele copia o shell do app para um cache; depois disso, responde às requisições primeiro a partir desse cache.

const VERSION = 'shift-notes-v1';
const SHELL = ['./', './index.html', './manifest.webmanifest', './icons/icon-192.png', './icons/icon-512.png'];

self.addEventListener('install', (e) => {
  e.waitUntil(caches.open(VERSION).then((c) => c.addAll(SHELL)).then(() => self.skipWaiting()));
});

self.addEventListener('fetch', (e) => {
  if (e.request.method !== 'GET') return;
  e.respondWith(
    caches.match(e.request, { ignoreSearch: true })
      .then((hit) => hit || fetch(e.request).catch(() => caches.match('./index.html')))
  );
});

Registre-o a partir da página, depois que ela carregar:

if ('serviceWorker' in navigator) {
  addEventListener('load', () => navigator.serviceWorker.register('sw.js'));
}

O catch da última linha importa mais do que parece. Quando a rede some e a requisição era uma navegação, ele devolve a página em cache em vez de deixar o navegador mostrar o próprio erro — que é exatamente a diferença que a próxima seção mede.

O app sobrevive mesmo à perda da rede?

Sim, e conferimos do jeito difícil. Cortar a rede do navegador não basta por si só, porque um servidor que continua rodando ainda pode estar respondendo de algum lugar. Então o teste corta a rede e para o servidor, e depois confirma de fora do navegador que a origem está realmente morta antes de recarregar.

✓ Chrome reports no installability errors    none
✓ a service worker is active                 state=activated
✓ the page is controlled by it               controller=true
✓ the shell is in the cache                  5 entries
✓ the origin is genuinely gone               ECONNREFUSED
✓ it still renders with the server stopped   h1="Shift Notes"
✓ it is still interactive offline            form and list present

first load            4430 bytes over the wire, 18 ms to load event
offline reload        0 bytes over the wire

Catorze verificações, todas passando, estáveis em três execuções consecutivas. O número que sustenta a afirmação é o último: 0 bytes pela rede num reload completo, enquanto um fetch simples à mesma origem, feito de fora do navegador, volta com ECONNREFUSED.

Como medimos: Node 22 controla um Chrome de verdade pelo protocolo do DevTools, com um perfil novo a cada execução. Network.emulateNetworkConditions coloca a página offline, o servidor estático é encerrado em vez de pausado, e o reload é um reload normal, não um carregamento só do cache.

Instalável e funciona-offline são duas propriedades diferentes, e só uma delas é o que uma pessoa quer dizer com “é um app”.

O que acontece se você pular o service worker?

O Chrome instala o app mesmo assim — e essa é a armadilha. Apagamos uma linha, a do registro, e rodamos as mesmas verificações.

O veredito de instalabilidade do Chrome não mudou: nenhum erro. Ele ainda ofereceria a instalação, ainda daria um ícone, ainda abriria em tela cheia sem barra de endereço.

Aí o reload offline caiu na página de erro do próprio Chrome. Nosso script lê o primeiro título da página, e ele voltou como h1="Press space to play" — o jogo do dinossauro. O reload também puxou 184.663 bytes por uma conexão que deveria estar morta, porque essa página de erro é, ela mesma, uma página.

O mesmo app instalado duas vezes, lado a lado num celular, com a rede cortada e o servidor parado: com o service worker ele mostra Shift Notes e suas três notas salvas, e sem ele a tela é a página de erro offline do Chrome, com o dinossauro e Press space to play

Então o service worker não é o passo opcional de acabamento. É o arquivo que torna a instalação honesta.

O que isso custa num iPhone?

Três coisas concretas, e você deve conhecê-las antes de prometer a alguém uma instalação sem atrito.

Não existe prompt de instalação. O Safari não mostra banner nem dispara evento que você possa capturar; o leitor precisa abrir o menu Compartilhar e escolher Adicionar à Tela de Início. Isso significa que o seu app tem que pedir isso na própria interface, num momento em que o leitor já o quer.

Notificações push funcionam, mas só depois. Elas chegaram no iOS 16.4 e só para um web app que já está na Tela de Início — um site numa aba do Safari não recebe push, qualquer que seja a permissão que ele tenha. A sincronização em segundo plano (background sync) continua indisponível, então tudo o que precisa acontecer com o app fechado precisa acontecer no seu servidor.

Nada disso impede o app de funcionar. Muda o que você pode prometer: um ícone, uma janela em tela cheia e uso offline são seus; comportamento silencioso em segundo plano, não.

Quando usar uma loja mesmo assim?

Quando uma de quatro coisas for verdade, e vale ser honesto sobre elas antes de se comprometer.

Você precisa do sistema de pagamento da loja, porque vender bens digitais dentro de um app iOS não é algo que dá para contornar. Você precisa de acesso profundo ao hardware — periféricos Bluetooth, localização em segundo plano, dados de saúde. Você conta com a busca da loja para encontrar usuários. Ou o processo de compras do seu cliente simplesmente exige uma listagem.

Se nenhuma delas for verdade, a loja é sobretudo um portão pelo qual você está pedindo para passar. E se uma delas se tornar verdade mais tarde, o backend que você constrói agora não muda — as mesmas contas e a mesma API atendem um cliente nativo quando você o escrever.

Onde entra o backend?

No momento em que duas pessoas precisam dos mesmos dados. O app do repositório guarda as notas em localStorage, o que é honesto para uma demo e errado para um produto: é por dispositivo, por navegador, invisível para você e some quando alguém limpa o cache.

É o mesmo muro em que o caminho da loja bate, só que sem a fila de revisão na frente. Contas, um banco de dados, armazenamento de arquivos e uma API são o que transforma a coisa na tela inicial em algo que dois celulares podem compartilhar.

Um backend gerenciado cobre isso sem mudar os três arquivos que você acabou de escrever: cadastro e login, um banco de dados com permissões por registro, e endpoints REST e GraphQL que a sua página pode chamar com fetch.

Adicionar cadastro e login é o próximo passo, e é o mesmo trabalho quer isso acabe numa loja, quer nunca acabe.

Como colocar o app no ar?

Sirva a pasta por HTTPS. É o único requisito rígido, e não é negociável: um service worker não registra por HTTP puro em lugar nenhum além de localhost, então sem um certificado a metade offline simplesmente não existe.

Qualquer hospedagem estática com HTTPS serve, e um container também — hospedar um site estático num container mede esse caminho de ponta a ponta, incluindo o arquivo que o serve.

Depois verifique o que você publicou, não o que você escreveu. Instale pelo navegador, desligue a rede e recarregue. Se você vê o seu app, terminou. Se vê um dinossauro, seu service worker não está registrado.

O que fazer primeiro?

Abra a pasta do seu app e adicione o manifest. É a menor mudança com o maior efeito visível — um arquivo e um <link>, e o navegador passa a oferecer a instalação de algo que antes era um favorito.

Depois adicione o service worker, porque a instalação sem ele é uma promessa que você não cumpriu. Depois rode as verificações contra um navegador de verdade em vez de um checklist, já que o checklist estava errado sobre os ícones e calado sobre a coisa que de fato quebra.

As lojas continuarão lá se você precisar delas. A diferença é que este caminho coloca seu app numa tela inicial hoje, e ninguém precisa dizer sim.

Tagspwaweb-appapp-storeservice-workerofflinedeployment

Perguntas frequentes

Preciso de uma loja de aplicativos para publicar um app?

Não. Um web app pode ser instalado na tela inicial de um celular, abrir em tela cheia com ícone próprio e sem barra de endereço, e funcionar sem conexão. Ninguém o revisa e ninguém pode removê-lo. O que você abre mão é da descoberta pela loja, dos sistemas de pagamento das lojas e de um punhado de capacidades nativas — a mais visível, a sincronização em segundo plano no iOS.

Por que o Google Play exige 12 testadores?

O Google exige que contas pessoais de desenvolvedor criadas depois de 13 de novembro de 2023 rodem um teste fechado com pelo menos 12 testadores participando sem interrupção por no mínimo 14 dias antes de poderem solicitar acesso à produção. A análise da solicitação normalmente leva sete dias ou menos. Contas de organização não estão sujeitas ao teste.

Por que a Apple recusa apps que são só um site?

A diretriz 4.2 do App Review diz que um app deve incluir recursos, conteúdo e interface que o elevem além de um site reempacotado, e a 4.2.2 diz que apps não devem ser principalmente recortes da web ou uma coleção de links. Embrulhar um site numa casca nativa e submetê-lo é o caso que a diretriz nomeia, então é o caso que os revisores procuram.

Um web app instalável é o mesmo que um que funciona offline?

Não, e essa é a armadilha. Removemos o registro do service worker de um app e o Chrome continuou sem reportar erros de instalabilidade — ele o instalaria. O reload offline então caiu na página de erro do próprio Chrome, que o nosso script capturou como um título dizendo Press space to play. Instalabilidade é sobre o manifest; funcionar offline é sobre o service worker.

Um web app instalado consegue enviar notificações push num iPhone?

Sim, desde o iOS 16.4, mas só depois que o site foi adicionado à Tela de Início — um site aberto numa aba do Safari não recebe push. Também não existe prompt de instalação no iOS: o leitor precisa usar Compartilhar e depois Adicionar à Tela de Início, então o app tem que pedir isso na própria interface.

Onde o backend entra num web app sem loja?

Exatamente onde entraria num app nativo. Os três arquivos daqui guardam as notas em localStorage, que é por dispositivo e por navegador, então nada é compartilhado e nada sobrevive a um cache limpo. Contas, um banco de dados e uma API substituem isso sem mudar os três arquivos, e o mesmo backend atende um app nativo depois, se um dia você for mesmo para uma loja.

Leitura complementar

Revisado e verificado por Back4app Engineering, Editorial review na Back4app

Testado em 27 de outubro de 2026 — Navegador: Chrome 155.0.8059.85 no macOS, arm64 · App: três arquivos estáticos, sem build · Harness: Node 22 pelo protocolo do DevTools · Backend: Back4app, plano Free.

Histórico de alterações

  • 28 de outubro de 2026 — O repositório complementar ainda não foi publicado, então o link para ele e o botão foram removidos; eles voltam quando ele for.
  • 28 de outubro de 2026 — O repositório complementar foi publicado, então o botão e o link no texto estão de volta.

Mais da Back4app Engineering

Containers · tutorial

How to Host a Static Website on Back4app Containers With Docker and nginx [Full Repo]

Backend · tutorial

How to Add Authentication to a Web or Flutter App Without Writing an Auth Server [Full Repo]

Backend · report

Por que apps feitos com vibe coding quebram: 13 problemas de 130.000 posts, em ordem de gravidade, com a solução de cada um