Web App Manifest: guia completo para criar, configurar e transformar qualquer site em um aplicativo instalável
Para acompanhar este artigo
Nível: Intermediário
Introdução
Imagine acessar um site pelo celular e poder instalá-lo na tela inicial, com nome, ícone e aparência próprios, sem baixar um aplicativo pela loja do sistema. Essa experiência é possível com Progressive Web Apps (PWA), e um dos seus componentes é o Web App Manifest.
O Manifest é um arquivo JSON que descreve como uma aplicação web deve ser identificada, apresentada e iniciada quando instalada em um dispositivo compatível. Ele pode ser utilizado em projetos PHP, JavaScript, Python, Node.js, Laravel, WordPress, React, Vue ou HTML puro.
Neste guia, vamos criar o arquivo, entender suas propriedades, configurar HTML e servidores, implementar instalação, conhecer o Service Worker, tratar cache e diagnosticar problemas. É importante entender desde o início que o Manifest não transforma o site em um aplicativo nativo e não garante, sozinho, a instalação.
O que é e para que serve o Web App Manifest?
É um documento JSON que fornece metadados ao navegador e ao sistema operacional. Pode definir nome, ícones, endereço inicial, escopo de navegação, cores, modo de exibição, orientação e atalhos. Com a configuração adequada e os critérios da plataforma atendidos, navegadores compatíveis podem oferecer a instalação do site.
O código da aplicação continua sendo web. O Manifest não implementa autenticação, banco de dados, notificações ou funcionamento offline. Essas funções pertencem a outras tecnologias.
Como funciona?
- O visitante acessa uma página do site.
- O navegador encontra a referência ao Manifest no HTML.
- Solicita o arquivo JSON ao servidor.
- Interpreta as propriedades e os recursos declarados.
- Avalia o suporte e os critérios de instalação da plataforma.
- Quando as condições são atendidas, pode oferecer a instalação.
O HTML aponta para o Manifest; o JSON descreve a aplicação; o navegador e o sistema operacional decidem como apresentar os recursos disponíveis.
Criando o arquivo manifest.webmanifest
Crie manifest.webmanifest na raiz pública do site. A extensão .webmanifest é recomendada. Também é possível utilizar um arquivo JSON com outro nome, desde que seja servido corretamente e referenciado no HTML.
public/
├── index.html
├── manifest.webmanifest
├── favicon.ico
├── uploads/
│ └── icons/
│ ├── icon-192.png
│ ├── icon-512.png
│ ├── icon-maskable-192.png
│ └── icon-maskable-512.png
├── js/
│ ├── app.js
│ └── install.js
└── service-worker.js
A estrutura é ilustrativa. Frameworks e hospedagens podem utilizar diretórios públicos diferentes.
Exemplo de Manifest
{
"id": "/",
"name": "Minha Aplicação",
"short_name": "MinhaApp",
"description": "Aplicação web para gerenciamento.",
"lang": "pt-BR",
"dir": "ltr",
"start_url": "/?source=pwa",
"scope": "/",
"display": "standalone",
"display_override": [
"standalone",
"minimal-ui",
"browser"
],
"orientation": "any",
"theme_color": "#1565C0",
"background_color": "#FFFFFF",
"icons": [
{
"src": "/uploads/icons/icon-192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "any"
},
{
"src": "/uploads/icons/icon-512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "any"
},
{
"src": "/uploads/icons/icon-maskable-192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "maskable"
},
{
"src": "/uploads/icons/icon-maskable-512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
}
],
"shortcuts": [
{
"name": "Página inicial",
"short_name": "Início",
"url": "/",
"description": "Abrir a página inicial"
},
{
"name": "Minha conta",
"short_name": "Conta",
"url": "/conta",
"description": "Acessar minha conta"
}
]
}
Os caminhos e rotas são exemplos: substitua-os pelos recursos reais do projeto. Não declare ícones que não existem.
Entendendo as propriedades
Identidade
id: identificador estável da aplicação. Defina-o desde o início e evite alterá-lo depois que usuários instalarem o site.name: nome completo apresentado em contextos de instalação.short_name: nome reduzido para espaços limitados.description: descrição que pode ser utilizada por interfaces de instalação. Não substitui a meta description do HTML e não é uma configuração direta de SEO.lang: idioma dos textos do Manifest, comopt-BR.dir: direção do texto:ltr,rtlouauto.
Navegação
start_url define a URL aberta quando o aplicativo é iniciado. Deve ser válida, acessível e adequada ao contexto da aplicação. Parâmetros de rastreamento podem ser usados, mas não devem causar redirecionamentos inesperados nem depender de uma sessão que não exista.
scope define o escopo de navegação associado à aplicação. Não é mecanismo de segurança, não bloqueia páginas e não substitui autorização no servidor. Navegar para fora do escopo pode fazer o navegador apresentar elementos adicionais, como a barra de endereço.
Se o projeto estiver em https://exemplo.com/sistema/, uma configuração possível é:
{
"id": "/sistema/",
"start_url": "/sistema/",
"scope": "/sistema/"
}
Os caminhos dos ícones e a referência ao Manifest também precisam considerar o subdiretório.
Apresentação
display define o modo de exibição preferencial:
browser: interface tradicional do navegador.standalone: aparência de aplicativo independente.minimal-ui: interface reduzida, quando suportada.fullscreen: solicita utilização de toda a tela.
Para muitas aplicações, standalone é adequado. A disponibilidade varia por navegador e sistema operacional.
display_override permite fornecer uma lista ordenada de modos preferidos. O navegador utiliza o primeiro que suporta e recorre a display quando nenhum modo da lista é compatível. É uma propriedade opcional.
orientation define uma orientação preferencial, como any, portrait ou landscape. Seu efeito depende da plataforma e não deve ser tratado como bloqueio universal de rotação.
theme_color define uma cor associada à interface do navegador ou sistema. background_color define uma cor de fundo utilizada em contextos de inicialização. Escolha cores coerentes com a primeira tela da aplicação.
Ícones
icons lista imagens que podem representar a aplicação. Cada item pode conter src, sizes, type e purpose. O campo sizes declara dimensões, mas não redimensiona a imagem real.
Para requisitos usuais de instalação dos navegadores Chromium, disponibilize ícones de 192 × 192 e 512 × 512 pixels. PNG é uma opção amplamente compatível.
purpose pode indicar any, maskable ou monochrome. Um ícone maskable precisa ter uma área segura ao redor do símbolo principal. Apenas declarar essa finalidade em uma imagem comum não a torna adequada para recortes. Teste a imagem em Maskable.app.
Atalhos
shortcuts permite declarar atalhos para páginas ou funções. Em plataformas compatíveis, eles podem aparecer em um menu associado ao ícone instalado. A quantidade e a apresentação dependem do navegador e do sistema.
"shortcuts": [
{
"name": "Meus pedidos",
"short_name": "Pedidos",
"description": "Consultar pedidos",
"url": "/pedidos",
"icons": [
{
"src": "/uploads/icons/pedidos.png",
"sizes": "96x96",
"type": "image/png"
}
]
}
]
Aplicativos relacionados
related_applications permite declarar aplicativos relacionados, como uma versão Android distribuída na Google Play:
"related_applications": [
{
"platform": "play",
"id": "com.exemplo.minhaapp",
"url": "https://play.google.com/store/apps/details?id=com.exemplo.minhaapp"
}
]
prefer_related_applications indica se o navegador deve preferir sugerir um aplicativo relacionado em vez da aplicação web. Se não houver aplicativo nativo, normalmente essas propriedades podem ser omitidas. Não configure essa preferência como true se a intenção for promover a instalação da aplicação web.
Preparando os ícones
| Arquivo | Dimensão | Uso |
|---|---|---|
| icon-192.png | 192 × 192 | Ícone de aplicação |
| icon-512.png | 512 × 512 | Ícone de alta resolução |
| icon-maskable-192.png | 192 × 192 | Ícone adaptável |
| icon-maskable-512.png | 512 × 512 | Ícone adaptável |
| apple-touch-icon.png | 180 × 180 | Compatibilidade adicional com Apple |
Use imagens quadradas, com boa resolução e contraste. Evite textos pequenos e elementos importantes próximos às bordas. Para verificar dimensões reais com ImageMagick:
identify public/uploads/icons/icon-192.png
identify public/uploads/icons/icon-512.png
Referenciando o Manifest no HTML
Adicione a referência dentro do elemento head do documento HTML:
<link rel="manifest" href="/manifest.webmanifest">
Exemplo de cabeçalho completo:
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="theme-color" content="#1565C0">
<meta name="description" content="Minha aplicação web.">
<title>Minha Aplicação</title>
<link rel="manifest" href="/manifest.webmanifest">
<link rel="icon" href="/favicon.ico">
<link rel="apple-touch-icon" href="/uploads/icons/apple-touch-icon.png">
</head>
<body>
<h1>Minha Aplicação</h1>
</body>
</html>
Em sites com várias páginas, disponibilize a referência nos documentos HTML relevantes. Se houver um cabeçalho compartilhado, insira a configuração no template central.
Configurando em diferentes tecnologias
O Manifest é independente da linguagem do backend. O navegador recebe HTML e solicita um arquivo JSON; não é necessário um módulo especial por linguagem.
PHP
Procure o arquivo responsável pelo cabeçalho comum e adicione a referência ao Manifest. Se o projeto utiliza templates, faça a inclusão no template principal. Não é necessário gerar o JSON dinamicamente com PHP, salvo quando houver necessidade real de configurações diferentes por ambiente ou usuário.
WordPress
Em um tema próprio, pode-se utilizar o hook wp_head:
add_action('wp_head', function () {
echo '<link rel="manifest" href="/manifest.webmanifest">';
});
Adicione o código ao local apropriado do tema ou a um plugin próprio. Se já existir integração PWA, verifique-a antes de adicionar outra referência.
React com Vite
No Vite, abra index.html e adicione a referência no head. Disponibilize o arquivo em public/manifest.webmanifest; o Vite copia os recursos públicos para a raiz da distribuição de produção.
Node.js com Express
Configure o diretório público:
const express = require("express");
const path = require("path");
const app = express();
app.use(express.static(path.join(__dirname, "public")));
app.listen(3000, () => {
console.log("Servidor iniciado");
});
O arquivo public/manifest.webmanifest poderá ser acessado em http://localhost:3000/manifest.webmanifest. Em produção, utilize HTTPS.
HTML puro
Coloque o arquivo na raiz pública e adicione a referência no HTML. Não há dependência de linguagem de servidor.
Configurando o servidor
O tipo MIME recomendado para arquivos .webmanifest é application/manifest+json.
Apache
Em um arquivo .htaccess ou na configuração apropriada, adicione:
AddType application/manifest+json .webmanifest
Verifique a resposta:
curl -I https://exemplo.com/manifest.webmanifest
Procure HTTP 200 e um cabeçalho Content-Type apropriado.
Nginx
Associe o tipo MIME no bloco http, normalmente no arquivo principal ou em um arquivo incluído:
http {
include /etc/nginx/mime.types;
types {
application/manifest+json webmanifest;
}
}
Se o arquivo mime.types já tiver outras associações, preserve-as. Não substitua a lista inteira. Valide e recarregue:
sudo nginx -t
sudo systemctl reload nginx
HTTPS é necessário para recursos que exigem contexto seguro. Durante o desenvolvimento, localhost e 127.0.0.1 são exceções reconhecidas. Não use file:// para validar a instalação.
Manifest e Service Worker
O Service Worker é um script JavaScript executado em um contexto separado da página. Pode atuar como intermediário entre a aplicação, o navegador e a rede, interceptar requisições, implementar cache e oferecer determinadas experiências offline.
O Manifest descreve a aplicação; o Service Worker implementa comportamentos de rede. O Service Worker não é requisito universal para instalar uma aplicação web. Ele é necessário quando queremos funcionalidades que dependem dele, como estratégias offline.
Exemplo de Service Worker
Crie service-worker.js na raiz pública. O exemplo usa estratégia de rede primeiro com fallback para cache. É didático e precisa ser adaptado antes de produção.
const CACHE_NAME = "minha-app-v1";
const STATIC_FILES = [
"/",
"/offline.html",
"/css/style.css",
"/js/app.js"
];
self.addEventListener("install", (event) => {
event.waitUntil(
caches.open(CACHE_NAME)
.then((cache) => cache.addAll(STATIC_FILES))
);
self.skipWaiting();
});
self.addEventListener("activate", (event) => {
event.waitUntil(
caches.keys().then((keys) => Promise.all(
keys
.filter((key) => key !== CACHE_NAME)
.map((key) => caches.delete(key))
))
);
self.clients.claim();
});
self.addEventListener("fetch", (event) => {
const request = event.request;
if (
request.method !== "GET" ||
new URL(request.url).origin !== self.location.origin
) {
return;
}
event.respondWith(
fetch(request)
.then((response) => {
if (response.ok && response.type === "basic") {
const copy = response.clone();
caches.open(CACHE_NAME)
.then((cache) => cache.put(request, copy));
}
return response;
})
.catch(async () => {
const cached = await caches.match(request);
if (cached) return cached;
if (request.mode === "navigate") {
return caches.match("/offline.html");
}
return Response.error();
})
);
});
O exemplo exige que os recursos listados e offline.html existam. Em aplicações autenticadas, painéis administrativos e sistemas financeiros, trate dados privados, respostas personalizadas, cookies e cabeçalhos de cache com cuidado. Não armazene respostas sensíveis indiscriminadamente. A limpeza deve remover somente caches pertencentes à aplicação.
Registrando o Service Worker
if ("serviceWorker" in navigator) {
window.addEventListener("load", async () => {
try {
const registration =
await navigator.serviceWorker.register("/service-worker.js");
console.log("Service Worker registrado:", registration.scope);
} catch (error) {
console.error("Falha ao registrar Service Worker:", error);
}
});
}
A raiz permite escopo amplo no domínio. Se a aplicação estiver em um subdiretório, ajuste o caminho e o escopo.
Implementando um botão de instalação
Em navegadores Chromium compatíveis, o evento beforeinstallprompt permite criar um botão próprio. Esse evento não é universal e não está disponível no Safari do iOS.
HTML
<button id="install-button" hidden>Instalar aplicativo</button>
JavaScript
let deferredPrompt = null;
const installButton = document.getElementById("install-button");
window.addEventListener("beforeinstallprompt", (event) => {
event.preventDefault();
deferredPrompt = event;
installButton.hidden = false;
});
installButton.addEventListener("click", async () => {
if (!deferredPrompt) return;
deferredPrompt.prompt();
const choice = await deferredPrompt.userChoice;
console.log("Resultado da instalação:", choice.outcome);
deferredPrompt = null;
installButton.hidden = true;
});
window.addEventListener("appinstalled", () => {
installButton.hidden = true;
deferredPrompt = null;
console.log("Aplicativo instalado.");
});
Inclua o arquivo com <script src="/js/install.js" defer></script>. O navegador avalia a possibilidade de instalação e pode disparar o evento. O código guarda o evento, mostra o botão e solicita a interface quando o usuário clica. Não chame prompt() antes de receber o evento.
Instalação no iPhone e iPad
No iOS, não dependa de beforeinstallprompt. A instalação normalmente é iniciada pelo usuário: no Safari, abra o site, toque no botão de compartilhamento, selecione adicionar à tela inicial e confirme. O comportamento e as opções variam conforme a versão do sistema.
Atualização e cache
Quando nome, ícone ou cores mudam, o navegador pode continuar mostrando a versão antiga porque o navegador, proxy ou CDN armazenou uma cópia. Verifique os cabeçalhos:
curl -I https://exemplo.com/manifest.webmanifest
Observe Cache-Control, ETag, Last-Modified e Age. Para um Manifest que muda ocasionalmente, uma política de revalidação pode ser adequada:
Cache-Control: no-cache
Isso permite armazenar o recurso, mas exige validação antes de reutilizá-lo conforme as regras HTTP. Para ícones com nomes versionados, pode-se utilizar:
Cache-Control: public, max-age=31536000, immutable
Quando a imagem mudar, publique um novo nome, como icon-192-v2.png, e atualize a referência no Manifest. Se o Service Worker utiliza versão de cache, atualize seu identificador e remova somente caches antigos da própria aplicação. Manifest e Service Worker possuem mecanismos de atualização diferentes.
Compatibilidade entre navegadores
Nem todos os navegadores implementam os mesmos recursos. O suporte varia por navegador, sistema operacional e versão.
- Chrome e Edge: oferecem recursos importantes de instalação, modo standalone, ícones e, em plataformas compatíveis, atalhos e
beforeinstallprompt. A instalação depende dos critérios aplicáveis. - Firefox: oferece suporte a partes das tecnologias PWA, mas a experiência de instalação não é equivalente à do Chrome em todas as plataformas.
- Safari no macOS: permite adicionar sites ao Dock como aplicações web, com integração diferente dos navegadores Chromium.
- Safari no iPhone e iPad: permite adicionar sites à tela inicial. O comportamento de abertura como aplicação web mudou em versões recentes do iOS e iPadOS e pode depender da versão e das opções do usuário.
Adote aprimoramento progressivo: implemente os recursos básicos e permita que cada navegador utilize aquilo que suporta. Valide o comportamento nas versões e dispositivos que seu público utiliza.
Manifest em subdiretórios
Para uma aplicação em https://exemplo.com/sistema/, ajuste os caminhos:
{
"id": "/sistema/",
"start_url": "/sistema/",
"scope": "/sistema/",
"display": "standalone",
"name": "Sistema",
"short_name": "Sistema",
"icons": [
{
"src": "/sistema/uploads/icons/icon-192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/sistema/uploads/icons/icon-512.png",
"sizes": "512x512",
"type": "image/png"
}
]
}
A referência HTML também deve apontar para /sistema/manifest.webmanifest. Se os caminhos mudam entre ambientes, centralize a configuração em vez de espalhar endereços absolutos pelo projeto.
Diagnóstico de problemas
Manifest não encontrado
Execute curl -I https://exemplo.com/manifest.webmanifest. Se retornar 404, revise localização do arquivo, caminho no HTML, configuração do servidor e regras de reescrita.
JSON inválido
Execute python3 -m json.tool manifest.webmanifest. Erros comuns incluem vírgula sobrando, aspas simples, comentários, chaves não fechadas e tipos incorretos. JSON não aceita comentários.
Ícones não aparecem
Teste cada endereço com curl. Confira código HTTP, tipo MIME, dimensões reais, formato, permissões e caminhos.
Aplicativo abre na página errada
Revise start_url e confirme que a URL pertence ao escopo definido.
Instalação não aparece
Confira HTTPS, nome, ícones, URL inicial, modo de exibição, estado da aplicação e critérios específicos da plataforma. A presença do Manifest não garante instalação.
Ícone antigo continua aparecendo
Verifique cache do navegador e CDN. Publique uma nova versão com outro nome e atualize o Manifest.
Navegação abre fora do aplicativo
Revise scope e rotas. O escopo não substitui autorização no backend.
Testando no Chrome DevTools
Abra o site no Chrome, pressione F12 e acesse Application. Na seção Manifest, confira dados interpretados, ícones e erros. Se houver Service Worker, examine a seção correspondente e o Cache Storage. Na aba Network, localize a requisição do Manifest e confira status HTTP e tipo MIME.
Comandos úteis:
# Verificar o Manifest
curl -I https://exemplo.com/manifest.webmanifest
# Baixar o Manifest
curl -o manifest.json https://exemplo.com/manifest.webmanifest
# Validar JSON
python3 -m json.tool manifest.json
# Verificar ícones
curl -I https://exemplo.com/uploads/icons/icon-192.png
curl -I https://exemplo.com/uploads/icons/icon-512.png
# Verificar Service Worker
curl -I https://exemplo.com/service-worker.js
Esses comandos verificam disponibilidade e entrega, mas não substituem testes de instalação em dispositivos reais. O Lighthouse ajuda a avaliar desempenho, acessibilidade e boas práticas; a auditoria antiga de PWA foi descontinuada e uma pontuação antiga não deve ser usada como único critério de instalação.
Segurança e boas práticas
- Não coloque informações confidenciais no Manifest: o arquivo é público.
- Não confunda
scopecom autorização: proteja rotas no backend. - Não armazene indiscriminadamente respostas autenticadas ou personalizadas no cache.
- Mantenha ícones acessíveis e caminhos corretos.
- Utilize HTTPS em produção.
- Verifique se o projeto ou algum plugin já fornece um Manifest antes de criar outro.
- Mantenha o identificador da aplicação estável.
Checklist de implantação
- Manifest criado e JSON validado.
- Ícones de 192 × 192 e 512 × 512 disponíveis.
- Ícones maskable preparados.
- Favicon e Apple Touch Icon configurados quando necessários.
- Referência ao Manifest adicionada às páginas relevantes.
- HTTPS habilitado e tipo MIME conferido.
- Arquivo e ícones retornam HTTP 200.
id,start_url,scopeedisplayrevisados.- Service Worker e estratégia offline testados quando implementados.
- Chrome, Edge, Firefox e Safari avaliados nas plataformas de destino.
- Instalação testada em dispositivos reais compatíveis.
Perguntas frequentes
Todo site pode utilizar Manifest?
Sim. Qualquer site pode disponibilizar um Manifest válido e referenciá-lo no HTML. Os recursos de instalação dependem do navegador, do sistema operacional e dos requisitos aplicáveis.
Preciso de PHP ou JavaScript para criar o Manifest?
Não. É um arquivo JSON estático que pode ser servido por qualquer servidor web.
Preciso de banco de dados?
Não. O Manifest não necessita de banco de dados.
Preciso de Service Worker para instalar?
Não em todos os navegadores e cenários. O Service Worker é utilizado principalmente para cache, controle de requisições e experiências offline.
Posso utilizar o mesmo Manifest em todas as páginas?
Sim. Uma aplicação normalmente utiliza um Manifest compartilhado, referenciado por todas as páginas relevantes.
O Manifest melhora o SEO?
Não é um recurso de otimização direta de posicionamento. Seu benefício está relacionado à experiência de uso e à possibilidade de instalação.
O site funciona offline depois de instalado?
Não automaticamente. É necessário implementar uma estratégia apropriada, geralmente com Service Worker e Cache Storage.
Posso transformar uma loja virtual em PWA?
Sim. Carrinho, pagamentos, autenticação e dados pessoais precisam de tratamento adequado de segurança e cache.
Conclusão
O Web App Manifest é simples na estrutura, mas importante para oferecer uma experiência mais integrada entre sites e dispositivos. Sua principal vantagem é a independência tecnológica: PHP, Node.js, Python, React, Vue, WordPress e HTML puro podem utilizar o mesmo formato JSON.
Uma implementação bem-feita exige mais do que criar o arquivo e adicionar uma referência ao HTML. É necessário configurar caminhos, ícones, escopo, inicialização, servidor e critérios de compatibilidade. Com Service Worker e estratégias de cache, o Manifest integra uma arquitetura capaz de oferecer experiências web mais próximas das aplicações tradicionais.
Comece pelo Manifest, valide sua interpretação e instalação e acrescente funcionalidades progressivamente, conforme a necessidade real do projeto.
Referências técnicas
- W3C – Web Application Manifest: especificação da tecnologia.
- MDN – Web App Manifest: documentação das propriedades.
- MDN – Making PWAs installable: requisitos e instalação.
- MDN – Service Worker API: funcionamento dos Service Workers.
- Chrome for Developers – Installable Manifest: orientações para aplicações instaláveis.
- WebKit – Features in Safari 26: novidades do Safari.
- Apple Support – Add a website to the Home Screen: instruções para iPhone.
- Maskable.app: teste de ícones adaptáveis.
Observação: o suporte às propriedades e os critérios de instalação podem mudar entre versões de navegadores e sistemas operacionais. Consulte a documentação oficial antes de implementar recursos específicos em produção.
Neste artigo
Retome suas leituras no histórico. Suas últimas visitas ficam salvas neste navegador, mesmo sem conta.
Este artigo foi útil?
Ajude-nos a melhorar avaliando este conteúdo.
Teste seus conhecimentos
Esta área é exclusiva para usuários cadastrados. Entre ou crie sua conta para responder ao teste.
Comentários
Ainda não há comentários. Seja o primeiro a participar!
Quer participar da conversa? Entre na sua conta para comentar.