Offline & Installable

PWA & Service Workers

Um Progressive Web App é instalado como um app nativo, funciona offline e carrega instantaneamente em visitas recorrentes. Service workers e um web manifest tornam isso possível.

advanced14 min readUpdated 15 de set. de 2026
sw.js
js
// sw.js
const CACHE = "app-v1";
const ASSETS = ["/", "/app.js", "/styles.css"];

self.addEventListener("install", (event) => {
  event.waitUntil(
    caches.open(CACHE).then((cache) => cache.addAll(ASSETS)),
  );
});

self.addEventListener("fetch", (event) => {
  event.respondWith(
    caches.match(event.request).then((cached) => cached || fetch(event.request)),
  );
});
Requer
HTTPS ou localhost
Manifest
manifest.webmanifest
Worker
service worker
Caching
Cache Storage API
Instalação
Adicionar à tela inicial
Principal benefício
Offline e recarregamento instantâneo

Por que importa

Por que criar um PWA

Funciona offline

Um service worker serve assets e dados em cache, para que o app continue funcionando quando a rede cair.

Visitas recorrentes instantâneas

Assets em cache carregam imediatamente, transformando uma primeira visita lenta em uma segunda visita instantânea.

Instalável

Com um manifest e um worker, os navegadores oferecem a instalação do app na tela inicial ou no desktop.

O panorama completo

As três partes de um PWA

Um manifest que descreve o app, um service worker que controla a rede e uma origem segura que torna ambos possíveis.

O manifest

Descrever

Nome, ícones, cores e modo de exibição dizem à plataforma como apresentar o app.

O service worker

Interceptar

Um script de background que fica entre a página e a rede, controlando as requisições.

O cache

Armazenar

A Cache Storage API armazena respostas que o worker pode servir posteriormente.

PWA em resumo

O núcleo de um PWA

Web manifest

Metadados que tornam o app instalável e controlam sua aparência.

Service worker

Um script que intercepta eventos de fetch e gerencia caches.

Cache Storage

Um armazenamento de chave-valor para pares de requisição e resposta.

Estratégia de caching

Cache-first, network-first, stale-while-revalidate e outras.

Fallback offline

Uma página ou estado exibido quando nada está em cache e a rede está fora do ar.

Background sync

Tenta reenviar requisições que falharam quando a conectividade retorna.

Uma breve historia

De hacks offline a web apps instaláveis

  1. 2014

    Proposta de service workers

    Um novo tipo de worker oferece às páginas uma camada de rede programável.

    14
  2. 2015

    Progressive Web Apps

    O termo é cunhado para descrever experiências web instaláveis e com capacidade offline.

    15
  3. 2018

    Suporte amplo nos navegadores

    Service workers e prompts de instalação chegam a todos os principais navegadores.

    18
  4. 2020

    Maturidade do Workbox

    A biblioteca Workbox do Google torna as estratégias de caching mais fáceis de implementar com segurança.

    20
  5. Hoje

    Mainstream

    Muitos sites importantes utilizam service workers para suporte offline e velocidade.

    Hoje

O guia completo

PWA & Service Workers: Tudo que voce precisa saber

O que é um PWA?

Um Progressive Web App é um site que se comporta como um aplicativo instalado: ele funciona offline, carrega instantaneamente em visitas recorrentes e pode ser adicionado à tela inicial ou ao desktop. Não existe uma tecnologia única chamada PWA — trata-se de um conjunto de capacidades que você adota incrementalmente.

Três ingredientes fazem isso funcionar:

  1. HTTPS, para que o navegador confie na sua origem o suficiente para permitir um service worker.
  2. Um web manifest que descreve o nome, ícones e modo de exibição do app.
  3. Um service worker, um script de segundo plano que intercepta requisições de rede e gerencia caches.

O resultado é uma experiência que parece nativa, mas continua sendo um site que você pode atualizar instantaneamente.

O web manifest

O manifest é um arquivo JSON vinculado ao head do HTML. Ele informa à plataforma como apresentar o app quando instalado.

{
  "name": "hackweb Reader",
  "short_name": "Reader",
  "start_url": "/",
  "display": "standalone",
  "background_color": "#0b0d0c",
  "theme_color": "#0b0d0c",
  "icons": [
    { "src": "/icons/192.png", "sizes": "192x192", "type": "image/png" },
    { "src": "/icons/512.png", "sizes": "512x512", "type": "image/png" }
  ]
}
<!-- index.html -->
<link rel="manifest" href="/manifest.webmanifest" />
<meta name="theme-color" content="#0b0d0c" />

display: "standalone" oculta a interface do navegador (browser chrome) quando instalado, e os ícones e cores controlam a splash screen e o alternador de tarefas. Um manifest válido somado a um service worker é o que faz com que o navegador ofereça a instalação do app.

O ciclo de vida do service worker

Um service worker é um script separado e sem acesso ao DOM. Ele possui um ciclo de vida distinto: install, activate e fetch.

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

Durante o install, você faz o pré-cache do app shell. Durante o activate, você limpa caches antigos. Durante o fetch, você intercepta requisições e decide como responder. Um novo worker aguarda até que todas as páginas que utilizam a versão anterior sejam fechadas antes de ser ativado, e é por isso que as atualizações às vezes exigem um segundo reload.

Estratégias de cache

O coração de um service worker é a sua estratégia de cache. Diferentes recursos exigem tratamentos diferentes.

  • Cache-first — serve do cache e, se não encontrar, recorre à rede. Ideal para assets versionados e imutáveis, como bundles com hash.
  • Network-first — tenta a rede e, em caso de falha, recorre ao cache. Bom para HTML e dados que precisam estar atualizados.
  • Stale-while-revalidate — serve a versão em cache imediatamente e a atualiza em segundo plano. Ótimo para dados onde um pequeno atraso na atualização é aceitável.
  • Cache-only e network-only — para casos específicos de borda.
// strategies.js
self.addEventListener("fetch", (event) => {
  const { request } = event;
  if (request.destination === "image") {
    event.respondWith(
      caches.match(request).then((cached) => cached || fetch(request)),
    );
  }
});

A propriedade request.destination informa qual tipo de recurso está sendo buscado, o que permite aplicar uma estratégia por tipo de asset.

Fallback offline

Mesmo com o cache, algumas requisições irão falhar. Forneça um fallback elegante.

// fallback.js
self.addEventListener("fetch", (event) => {
  if (event.request.mode === "navigate") {
    event.respondWith(
      fetch(event.request).catch(() => caches.match("/offline.html")),
    );
  }
});

Uma página offline, um shell em cache ou um estado claro de “você está offline” é muito melhor do que o erro padrão do navegador. Combinado com o background sync, as requisições que falharam offline podem ser tentadas automaticamente assim que a conectividade for restaurada.

Atualizações e versionamento

O cache é poderoso, mas fácil de configurar incorretamente. A regra é simples: versione seus caches e delete os antigos ao ativar. Caso contrário, os usuários podem ficar presos a arquivos obsoletos indefinidamente.

// activate.js
const CACHE = "app-v2";

self.addEventListener("activate", (event) => {
  event.waitUntil(
    caches.keys().then((keys) =>
      Promise.all(keys.filter((k) => k !== CACHE).map((k) => caches.delete(k))),
    ),
  );
});

Para qualquer coisa além do básico, a biblioteca Workbox gerencia precaching, roteamento, expiração e limpeza com padrões bem testados, além de se integrar à maioria das ferramentas de build.

Além do modo offline

Service workers permitem mais do que apenas cache:

  • Background sync tenta reenviar requisições que falharam quando a rede retorna.
  • Push notifications entregam mensagens mesmo quando o app está fechado.
  • Periodic sync atualiza o conteúdo em segundo plano.
  • Share target permite que seu app receba conteúdo compartilhado do sistema operacional.

Cada um desses recursos exige a permissão do usuário e deve ser usado com moderação, mas são eles que fazem um PWA parecer um app nativo de primeira classe.

Melhores práticas

  • Sirva via HTTPS e registre o worker após o carregamento.
  • Versione os caches e exclua os antigos no evento activate.
  • Use a estratégia cache-first para assets com hash e network-first para HTML.
  • Sempre forneça um fallback para modo offline.
  • Faça o precache apenas do app shell, não do site inteiro.
  • Considere usar o Workbox em vez de escrever estratégias complexas manualmente.
  • Teste o modo offline e as atualizações antes de fazer o deploy.

Erros comuns

  • Fazer cache de HTML para sempre e servir páginas desatualizadas.
  • Esquecer de limpar caches antigos após um deploy.
  • Fazer cache de respostas cross-origin opacas sem entender suas limitações.
  • Fazer precaching de tudo e inflar a primeira instalação.
  • Presumir que o novo worker é ativado imediatamente.
  • Ignorar a cota de armazenamento e deixar os caches crescerem sem controle.

Próximos passos

Um service worker é o maior ganho individual de performance para visitas recorrentes e a base do suporte offline. Combine-o com as técnicas do guia de Web Performance, entenda as requisições HTTP que ele intercepta e sirva o app a partir de um backend Node.js. Depois, adicione um manifest e um worker simples de cache-first a um site existente e veja-o funcionar offline.

Escolhendo uma estratégia de caching

Cache-first é rápido e ideal para offline em assets versionados. Network-first é adequado para conteúdo que deve estar sempre atualizado.

Cache-first
// hashed assets never change
self.addEventListener("fetch", (event) => {
  event.respondWith(
    caches.match(event.request).then(
      (cached) => cached || fetch(event.request),
    ),
  );
});
Network-first
// for HTML or fresh data:
// try the network, fall back
event.respondWith(
  fetch(event.request).catch(() =>
    caches.match(event.request),
  ),
);

Atualizando assets em cache

Altere o nome do cache a cada deploy e delete caches antigos para que os usuários não fiquem presos a arquivos obsoletos.

Preferível
const CACHE = "app-v2";

self.addEventListener("activate", (event) => {
  event.waitUntil(
    caches.keys().then((keys) =>
      Promise.all(
        keys
          .filter((k) => k !== CACHE)
          .map((k) => caches.delete(k)),
      ),
    ),
  );
});
Evitar
// same cache name forever,
// old files never replaced

Perguntas frequentes

Perguntas frequentes

Keep learning

Related topics from the roadmap.

$ comecar a aprender

Pronto para aprender PWA & Service Workers?

Nosso tutorial interativo te guia por PWA & Service Workers passo a passo — com quizzes e codigo real que voce pode executar no navegador.