# 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.

URL: https://www.back4app.com/blog/pt/como-publicar-seu-app-sem-as-lojas-de-aplicativos
Publicado: 2026-11-08 · Testado: 2026-10-27
Testado em: 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
Publisher: Back4app Engineering

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.

## 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.

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.

```json
{
  "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:

```html
<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 mudamos | Veredito do Chrome |
| --- | --- |
| ícones em `192` e `512` | instalável |
| só um ícone `192` | instalável |
| só um ícone `512` | instalável |
| nenhum ícone | `manifest-missing-suitable-icon, no-acceptable-icon` |
| `display: browser` | `manifest-display-not-supported` |
| sem `short_name` | instalá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](https://github.com/templates-back4app/installable-web-app).

## 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.

```js
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:

```js
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.

```text
✓ 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.

## 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](/blog/blog-assets/publish-your-app-without-the-app-stores/installed-offline-with-and-without-sw.png)

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](https://www.back4app.com/blog/add-signup-login-password-reset-without-an-auth-server) é 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](https://www.back4app.com/blog/host-a-static-website-in-a-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.
