O que acontece quando você digita uma URL no browser? - Parte 1: Da URL à resposta HTTP

Você digita uma URL no browser, aperta Enter e, alguns segundos depois, uma página aparece na tela.

Para quem trabalha com frontend, é fácil pensar que uma aplicação começa quando o JavaScript começa a ser executado. Mas o browser começa a trabalhar muito antes disso.

Antes que o primeiro componente seja hidratado, que o primeiro fetch() da aplicação seja executado ou que o bundle principal comece a rodar, o browser já pode ter interpretado a URL, consultado mecanismos de cache, resolvido DNS, estabelecido uma conexão, negociado segurança, enviado uma requisição HTTP e começado a receber uma resposta.

Este post é a primeira parte de uma série sobre o caminho entre uma URL e os pixels exibidos na tela. Nesta parte, vamos acompanhar o processo até a resposta HTTP:

URL
→ cache / service worker
→ DNS
→ conexão
→ requisição HTTP
→ CDN / servidor
→ resposta HTTP

Esse é um modelo simplificado. HTTP/1.1 e HTTP/2 normalmente utilizam TCP e TLS, enquanto HTTP/3 usa QUIC sobre UDP e altera alguns detalhes do estabelecimento da conexão.

A ideia é entender esse processo a partir da própria Web Platform: URLs, origins, cache, service workers, DNS, conexões, TLS, HTTP, headers e DevTools. Frameworks rodam sobre essas fundações, e entendê-las torna muito mais fácil investigar problemas de performance, segurança e carregamento.


1. Interpretando a URL e determinando a origin

Imagine que você acesse:

https://example.com/products?id=123#reviews

O browser não trata isso como uma string qualquer. Ele interpreta a URL e a divide em componentes:

scheme/protocol: https
hostname:        example.com
pathname/path:   /products
query/search:    ?id=123
fragment/hash:   #reviews

O scheme, exposto como protocol pela API URL do JavaScript, define como o recurso deve ser acessado. Em uma página web comum, normalmente será HTTPS.

O hostname identifica o domínio ou endereço do host. O pathname identifica o caminho dentro daquela origin. A query string, exposta como search, carrega parâmetros adicionais, enquanto o fragment, exposto como hash, aponta para uma posição ou estado dentro do documento.

Um detalhe importante: o fragment normalmente não é enviado ao servidor como parte da requisição HTTP. Ele é tratado pelo browser no lado do cliente.

A partir da URL, o browser também consegue determinar sua origin. Uma origin é definida pela combinação de scheme, host e porta.

Por exemplo:

https://example.com
http://example.com

Essas URLs possuem origins diferentes porque os schemes são diferentes.

O mesmo acontece aqui:

https://example.com
https://api.example.com

Os schemes são iguais, mas os hosts são diferentes.

E aqui:

https://example.com
https://example.com:8443

O scheme e o host são os mesmos, mas as portas são diferentes. Portas padrão são normalizadas ao comparar origins, então https://example.com e https://example.com:443 são considerados a mesma origin.

Origin é um dos conceitos mais importantes da segurança no browser. Storage, service workers, permissões, CORS e a Same-Origin Policy dependem, de maneiras diferentes, dos limites entre origins.

Você pode inspecionar os componentes de uma URL diretamente no JavaScript:

const url = new URL("https://example.com/products?id=123#reviews");

console.log(url.protocol); // "https:"
console.log(url.hostname); // "example.com"
console.log(url.host); // "example.com"
console.log(url.pathname); // "/products"
console.log(url.search); // "?id=123"
console.log(url.hash); // "#reviews"
console.log(url.origin); // "https://example.com"

Também é útil entender a diferença entre host e hostname:

const url = new URL("https://example.com:8443/products");

console.log(url.host); // "example.com:8443"
console.log(url.hostname); // "example.com"
console.log(url.port); // "8443"

host inclui a porta quando ela está presente. hostname contém apenas o domínio ou endereço do host.


2. Antes da rede: cache e service workers

Depois de interpretar a URL, o browser precisa determinar como obter o recurso. Uma requisição de rede pode ser necessária, mas nem toda navegação ou carregamento começa do zero.

Browsers possuem diferentes camadas de cache e mecanismos relacionados, incluindo:

  • HTTP cache;
  • caches de memória e disco;
  • back-forward cache, ou bfcache;
  • Cache API;
  • service workers.

Esses mecanismos não são necessariamente consultados em uma ordem fixa e universal. O comportamento depende do browser, do tipo de recurso, da navegação, do modo da requisição, dos headers de cache e do estado atual da página.

O HTTP cache é controlado principalmente pelas regras de cache do HTTP e por headers da resposta como Cache-Control, ETag e Last-Modified.

Cache-Control: max-age=3600
ETag: "abc123"
Last-Modified: Wed, 01 Jul 2026 12:00:00 GMT

O bfcache funciona de outra forma. Em vez de armazenar respostas individuais da rede, ele pode preservar uma página inteira em memória para que o browser a restaure rapidamente quando o usuário volta ou avança no histórico.

A Cache API é um armazenamento programável para pares de requisição e resposta. Ela é frequentemente utilizada em conjunto com service workers.

Um service worker não é simplesmente outro tipo de cache. Ele é um worker programável capaz de interceptar requisições dentro do seu escopo e decidir como respondê-las: usando a rede, a Cache API ou alguma outra estratégia.

Um service worker também não intercepta automaticamente a primeira visita a um site. Para controlar uma navegação, normalmente ele precisa já ter sido instalado, ativado e assumido o controle da página. Um primeiro acesso real geralmente ainda precisará da rede.

Um service worker simples usando uma estratégia cache first poderia ser:

const CACHE_NAME = "app-shell-v1";
const ASSETS = ["/", "/styles.css", "/main.js"];

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

self.addEventListener("fetch", (event) => {
  event.respondWith(
    caches.match(event.request).then((cachedResponse) => {
      return cachedResponse || fetch(event.request);
    }),
  );
});

Essa estratégia é chamada de cache first porque consulta o cache antes de recorrer à rede.

Cache pode tornar visitas seguintes muito mais rápidas, mas uma configuração incorreta também pode causar HTML desatualizado, assets antigos, deploys quebrados e comportamentos inconsistentes entre usuários. Por isso, entender de onde uma resposta veio é uma parte importante do debugging no frontend.


3. DNS: descobrindo onde se conectar

Se o browser precisar acessar a rede, ele terá que descobrir onde o servidor de destino pode ser encontrado.

Nós normalmente trabalhamos com nomes de domínio como example.com. A comunicação de rede, porém, precisa chegar a um endereço IP.

DNS, ou Domain Name System, é o sistema que relaciona nomes de domínio a informações como endereços IPv4 e IPv6.

Um fluxo simplificado de resolução pode ser representado assim:

cache do browser / sistema operacional
→ resolvedor DNS recursivo
→ root servers
→ TLD servers
→ authoritative nameserver
→ endereço IP

O browser normalmente não entra em contato diretamente com todos os servidores dessa hierarquia. Ele pode consultar caches locais e depois depender do sistema operacional ou de um resolvedor DNS configurado. O resolvedor recursivo pode já possuir a resposta em cache. Caso contrário, ele pode consultar a hierarquia do DNS em nome do cliente.

Os root servers ajudam a localizar os servidores responsáveis por um top-level domain, como .com ou .org. Os TLD servers então direcionam o resolvedor para o authoritative nameserver responsável pelo domínio solicitado.

Na prática, o uso de cache faz com que muitas consultas sejam resolvidas sem percorrer toda essa hierarquia.

Por que DNS importa para frontend engineers?

DNS adiciona trabalho antes que o browser consiga se conectar a um novo hostname. Se uma página depende de muitas origins externas, como serviços de analytics, fontes, hosts de imagens, APIs e widgets de terceiros, o browser pode precisar realizar resoluções DNS e conexões adicionais.

Cada origin externa pode introduzir custos relacionados a:

  • resolução DNS;
  • estabelecimento da conexão;
  • negociação TLS;
  • priorização de recursos;
  • políticas de segurança;
  • disponibilidade de serviços de terceiros.

Resource hints podem ajudar quando utilizados de forma intencional:

<link rel="dns-prefetch" href="https://fonts.gstatic.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />

dns-prefetch sugere que a resolução DNS seja feita antecipadamente. preconnect vai além e pede ao browser que prepare uma conexão com aquela origin antes que ela seja necessária, podendo incluir a negociação TLS no caso de HTTPS.

Esses hints devem ser reservados para origins realmente importantes para o carregamento inicial. Preconnections em excesso podem desperdiçar recursos e competir com trabalhos mais críticos.


4. Estabelecendo a conexão

Depois que o browser sabe onde se conectar, ele precisa estabelecer uma conexão de transporte antes de trocar mensagens HTTP.

Para HTTP/1.1 e HTTP/2 sobre HTTPS, isso normalmente significa TCP seguido de TLS.

TCP

TCP, ou Transmission Control Protocol, oferece comunicação confiável e ordenada entre dois endpoints. Antes que os dados da aplicação possam ser trocados, cliente e servidor estabelecem uma conexão por meio do three-way handshake:

Client → SYN     → Server
Client ← SYN/ACK ← Server
Client → ACK     → Server

Os detalhes exatos do protocolo vão além do que a maioria dos frontend engineers precisa no dia a dia. O principal ponto relacionado à performance é entender que estabelecer uma nova conexão exige round trips pela rede.

Quanto mais distante o usuário estiver do servidor, maior será o custo desses round trips. Esse é um dos motivos pelos quais CDNs, edge servers, reutilização de conexões e versões mais modernas do HTTP podem melhorar a performance de carregamento.

TLS

Se a página utiliza HTTPS, cliente e servidor também estabelecem um canal seguro usando TLS, ou Transport Layer Security.

TLS oferece três propriedades que importam diretamente para a Web:

  • autenticação — o browser consegue validar a identidade do servidor;
  • confidencialidade — o tráfego é criptografado;
  • integridade — alterações no tráfego durante o transporte podem ser detectadas.

Durante a negociação TLS, cliente e servidor concordam sobre parâmetros criptográficos, validam o certificado do servidor e estabelecem chaves para a comunicação protegida.

HTTPS não é uma troca opcional entre segurança e performance. Ele é um requisito fundamental de segurança para a Web moderna, e muitos recursos da Web Platform exigem um secure context.

O que muda com HTTP/3?

HTTP/3 não utiliza TCP. Ele roda sobre QUIC, que utiliza UDP e incorpora mecanismos de transporte confiável junto com TLS 1.3 ao processo de estabelecimento da conexão QUIC.

Portanto, este modelo mental:

DNS
→ TCP
→ TLS
→ HTTP

é útil para HTTP/1.1 e HTTP/2, mas não representa exatamente o funcionamento do HTTP/3.


5. Enviando a requisição HTTP

Quando a conexão está pronta, o browser pode solicitar o documento ao servidor.

Uma requisição simplificada de navegação em HTTP/1.1 poderia ser:

GET /products?id=123 HTTP/1.1
Host: example.com
Accept: text/html
Accept-Language: pt-BR
Accept-Encoding: gzip, br, zstd

O método GET indica que o cliente deseja obter uma representação de um recurso.

O request target /products?id=123 contém o pathname e a query string. Perceba que o fragment da URL original, como #reviews, não está incluído.

Os request headers fornecem contexto adicional. Accept descreve os tipos de mídia que o cliente consegue processar. Accept-Language informa preferências de idioma. Accept-Encoding lista os content encodings suportados. Cookies aplicáveis à requisição também podem ser enviados por meio do header Cookie.

HTTP/2 e HTTP/3 codificam as requisições de forma diferente do HTTP/1.1. Em vez da representação tradicional com uma request line, eles utilizam pseudo-headers como:

:method: GET
:scheme: https
:authority: example.com
:path: /products?id=123

A semântica continua familiar mesmo quando a representação na rede muda: o browser identifica o método, a origin de destino, o caminho e os metadados da requisição e envia essas informações ao servidor.


6. CDN, edge e origin

A requisição não necessariamente vai diretamente para o servidor que gerou a aplicação.

Muitos sites colocam uma CDN, ou Content Delivery Network, entre os usuários e o origin server.

Um caminho simplificado poderia ser:

Browser
→ CDN edge
→ origin server

Um edge server fica geograficamente mais próximo dos usuários do que o servidor de origem pode estar. Se ele já possui uma resposta válida em cache, consegue retorná-la sem consultar o origin server:

Browser
→ CDN edge
→ resposta em cache

Se a CDN não possui uma resposta válida em cache, ocorre um cache miss, e o edge pode buscar o recurso no servidor de origem:

Browser
→ CDN edge
→ origin server
→ CDN edge
→ Browser

Isso é especialmente útil para arquivos estáticos como CSS, JavaScript, imagens e fontes. HTML dinâmico também pode ser armazenado no edge quando a estratégia de cache da aplicação permite.

Essa parte do caminho está diretamente relacionada ao TTFB, ou Time to First Byte: o tempo entre o início da medição da navegação ou requisição e o momento em que o primeiro byte da resposta fica disponível para o browser.

Um TTFB alto pode ter várias causas, incluindo:

  • latência de rede;
  • DNS e estabelecimento da conexão;
  • redirects;
  • cache miss na CDN;
  • servidores sobrecarregados;
  • server-side rendering caro;
  • queries lentas no banco de dados;
  • chamadas a serviços externos.

TTFB é um sinal, não um diagnóstico. Ele mostra que o browser precisou esperar, mas não explica automaticamente o motivo.


7. Recebendo a resposta HTTP

Depois de processar a requisição, o servidor retorna uma resposta HTTP.

Uma resposta simplificada em HTTP/1.1 poderia ser:

HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Cache-Control: max-age=3600
Content-Encoding: br
Set-Cookie: session=abc; HttpOnly; Secure; SameSite=Lax

<!doctype html>
<html lang="pt-BR">
  <head>
    <title>Example</title>
  </head>
  <body>
    <h1>Hello</h1>
  </body>
</html>

Em alto nível, uma resposta HTTP contém três coisas:

  1. status — o que aconteceu;
  2. headers — metadados e instruções;
  3. body — o conteúdo propriamente dito.

Status codes comuns incluem 200 OK, redirects como 301 e 302, 304 Not Modified, erros do cliente como 404 Not Found e erros do servidor como 500 Internal Server Error.

Para frontend engineers, alguns response headers são especialmente importantes.

Content-Type

Content-Type informa ao browser que tipo de dado foi recebido, por exemplo text/html, text/css, application/json ou image/png.

Um content type incorreto pode fazer com que um recurso seja interpretado de forma errada ou até rejeitado completamente.

Cache-Control

Cache-Control define o comportamento de cache.

Um asset estático versionado pode utilizar uma política como:

Cache-Control: public, max-age=31536000, immutable

Isso funciona bem para nomes de arquivo que mudam sempre que o conteúdo muda, como /assets/main.8f3a2.js.

HTML normalmente exige uma estratégia mais cuidadosa, porque um documento desatualizado pode referenciar assets que já não existem depois de um deploy.

Validators como ETag e Last-Modified também permitem que o browser revalide conteúdo armazenado em cache e receba uma resposta 304 Not Modified quando o recurso não mudou.

Content-Encoding

Content-Encoding indica que o body da resposta foi codificado, normalmente com gzip, br (Brotli) ou zstd.

Compressão pode reduzir significativamente a quantidade de bytes transferidos em recursos baseados em texto.

Set-Cookie permite que o servidor armazene cookies no browser. Atributos como HttpOnly, Secure, SameSite, Path, Domain, Max-Age e Expires controlam o comportamento desses cookies.

Headers de segurança como Content-Security-Policy e headers relacionados a respostas cross-origin também são importantes. CORS, em particular, é aplicado pelo browser: quando as permissões cross-origin necessárias não estão presentes, o JavaScript pode ser impedido de ler uma resposta. Para requisições que exigem preflight, o browser pode primeiro enviar uma requisição OPTIONS e se recusar a enviar a requisição principal caso esse preflight falhe.

O body de uma navegação de documento normalmente é HTML. Quando o browser recebe esse HTML, começa a próxima etapa: interpretar o documento, descobrir recursos adicionais, construir as estruturas de renderização e, eventualmente, desenhar pixels na tela.


8. Observando o processo no DevTools

O lugar mais útil para observar esse caminho é o painel Network do DevTools do browser.

Faça este exercício:

  1. Abra uma janela privada ou anônima.
  2. Abra o DevTools.
  3. Vá até o painel Network.
  4. Ative Disable cache se quiser observar um carregamento de rede sem cache.
  5. Acesse um site.
  6. Selecione a requisição principal do documento.
  7. Analise Timing.
  8. Analise os request e response Headers.

Depois, tente responder:

  • Houve algum redirect?
  • A resposta veio de algum cache?
  • Qual protocolo foi utilizado?
  • Quanto tempo DNS e estabelecimento da conexão levaram?
  • Qual foi o TTFB?
  • Qual Content-Type foi retornado?
  • Qual política de Cache-Control foi aplicada?
  • A resposta estava comprimida?
  • Cookies foram enviados ou definidos?
  • Headers de segurança estavam presentes?

Também é possível observar partes da navegação programaticamente por meio da Navigation Timing API:

const [navigation] = performance.getEntriesByType("navigation");

if (navigation) {
  console.table({
    dns: navigation.domainLookupEnd - navigation.domainLookupStart,
    connection: navigation.connectEnd - navigation.connectStart,
    tls:
      navigation.secureConnectionStart > 0
        ? navigation.connectEnd - navigation.secureConnectionStart
        : 0,
    requestToFirstByte: navigation.responseStart - navigation.requestStart,
    responseDownload: navigation.responseEnd - navigation.responseStart,
  });
}

Alguns valores podem ser zero porque determinada etapa não foi necessária, uma conexão existente foi reutilizada ou o browser expõe a informação de maneira diferente. O objetivo não é decorar cada campo, mas relacionar o que o DevTools mostra com o processo de carregamento que acontece por baixo.


9. Por que frontend engineers deveriam se importar?

Todo esse caminho acontece antes que o browser consiga processar e renderizar completamente a aplicação.

Isso significa que muitos problemas que parecem ser de “performance do frontend” podem, na verdade, acontecer antes mesmo de o JavaScript se tornar relevante.

Uma página lenta pode ser causada por:

  • um redirect desnecessário;
  • resolução DNS lenta;
  • origins externas em excesso;
  • estabelecimento repetido de conexões;
  • cache miss na CDN;
  • alto tempo de processamento no servidor;
  • regras de cache mal configuradas;
  • uma resposta HTML grande e sem compressão.

O mesmo vale para segurança e debugging.

Um erro de CORS não é um erro do React. Uma violação de CSP não é um erro do bundler. Um deploy desatualizado pode ser um problema de cache. Um Content-Type incorreto pode fazer o browser rejeitar um arquivo perfeitamente válido. Um problema com cookies pode estar relacionado a SameSite, Secure, domínio ou path, e não ao estado da aplicação.

Frameworks podem gerar HTML, configurar headers, definir rotas, executar middleware, otimizar assets e integrar a aplicação com CDNs. Mas o browser continua trabalhando com URLs, origins, requisições HTTP, respostas HTTP, regras de cache e políticas de segurança.

Quando algo está lento ou quebrado, dizer apenas que “o framework está lento” raramente é um diagnóstico útil. Abra o painel Network e identifique onde o tempo está sendo gasto ou onde a falha realmente acontece.


Conclusão

Quando você digita uma URL no browser, uma quantidade surpreendente de trabalho acontece antes que a aplicação consiga renderizar qualquer coisa.

O browser interpreta a URL e determina sua origin. Caches existentes ou um service worker podem atender à requisição sem uma viagem completa pela rede. Caso contrário, o DNS resolve o hostname, o browser estabelece uma conexão, o HTTP transporta a requisição por uma infraestrutura que pode incluir uma CDN e o servidor retorna uma resposta contendo status, headers e body.

Um modelo mental útil é:

URL
→ mecanismos locais do browser
→ DNS
→ conexão
→ requisição HTTP
→ CDN / origin
→ resposta HTTP

Entender esse caminho facilita a investigação de performance de carregamento, bugs de cache, políticas de segurança, problemas de deploy e comportamentos inesperados do browser.

E essa é apenas a primeira metade da história.

Na próxima parte, o browser já terá recebido o HTML e precisará transformá-lo, junto com CSS e JavaScript, em algo que o usuário consiga ver:

HTML
→ DOM
→ CSSOM
→ render tree
→ layout
→ paint
→ composite
→ pixels na tela

Fontes recomendadas

  • WHATWG URL Standard
  • WHATWG Fetch Standard
  • MDN: URL API
  • MDN: Same-Origin Policy
  • MDN: Service Worker API
  • MDN: Cache API
  • MDN: PerformanceNavigationTiming
  • MDN: Cross-Origin Resource Sharing (CORS)
  • MDN: Content Security Policy (CSP)
  • RFC 1034 e RFC 1035: DNS
  • RFC 8446: TLS 1.3
  • RFC 9110: HTTP Semantics
  • RFC 9111: HTTP Caching
  • RFC 9112: HTTP/1.1
  • RFC 9113: HTTP/2
  • RFC 9114: HTTP/3
  • RFC 9293: TCP
  • Chrome DevTools Network panel documentation