Se você roda PHP em produção com Nginx + PHP-FPM, sabe como funciona: cada request dispara um processo, carrega o framework inteiro, monta o container de dependências, executa a lógica, responde, e descarta tudo. Na próxima request, repete do zero. Esse modelo share-nothing serviu bem por duas décadas, mas cobra um preço em latência e consumo de recursos que ficou difícil de ignorar.
Em maio de 2025, a PHP Foundation oficializou o suporte ao FrankenPHP, transferindo o repositório para a organização php/ no GitHub. O projeto de Kévin Dunglas deixou de ser uma aposta individual para virar infraestrutura oficial do ecossistema. E os números explicam o motivo.
O que é FrankenPHP e por que a PHP Foundation o adotou
FrankenPHP é um app server PHP escrito em Go que embute o interpretador PHP diretamente dentro do Caddy, o web server que já traz HTTP/3, HTTPS automático e compressão Zstandard de fábrica. Em vez de depender do FastCGI para comunicar Nginx com PHP-FPM, o FrankenPHP elimina essa camada intermediária: o PHP roda dentro do mesmo processo que serve HTTP.
O projeto acumula mais de 8.000 stars no GitHub e mais de 100 contribuidores. A adoção pela PHP Foundation em maio de 2025 significou a transferência do código-fonte e parte da documentação para a organização oficial, com manutenção compartilhada entre Kévin Dunglas, Robert Landers e Alexander Stecher. Provedores como Laravel Cloud, Upsun e Clever Cloud já o hospedam nativamente.
O que me chama atenção nessa movimentação é o pragmatismo. A Foundation não adotou o FrankenPHP por ser novidade, mas porque frameworks como Laravel, Symfony e Yii já ofereciam integrações oficiais com worker mode antes mesmo da transferência. O ecossistema votou com código.
Arquitetura: Go, Caddy e o embed do interpretador PHP
A arquitetura do FrankenPHP pode ser resumida assim: o interpretador C do PHP é compilado como uma biblioteca e carregado dentro de um binário Go via CGO (a bridge entre Go e C). Esse binário Go é um módulo do Caddy.
O resultado é um único processo que:
- Serve HTTP/1.1, HTTP/2 e HTTP/3
- Gera e renova certificados TLS automaticamente via ACME
- Comprime respostas com Zstandard, Brotli ou Gzip
- Suporta 103 Early Hints (o FrankenPHP é o único SAPI PHP com esse recurso)
- Executa código PHP sem FastCGI
A configuração mínima em produção cabe em três linhas de Caddyfile:
{
frankenphp
}
localhost {
root * /app/public
php_server
}
Isso entrega HTTPS automático, HTTP/3 e compressão sem configuração adicional. Compara com o setup equivalente de Nginx + PHP-FPM + Certbot + módulos de compressão, e a diferença de complexidade operacional fica evidente.
Como o FrankenPHP também é uma biblioteca Go standalone (net/http compatible), ele pode ser embarcado em qualquer aplicação Go que precise executar PHP. Não é o caso de uso mais comum, mas abre possibilidades para quem mantém backends híbridos.
Worker mode: boot uma vez, sirva milhares
O modo clássico do FrankenPHP funciona como um drop-in replacement do PHP-FPM: cada request carrega e descarta o estado PHP normalmente. É útil para migração gradual, mas não é onde o ganho real mora.
O worker mode muda a equação. Em vez de descartar tudo entre requests, o FrankenPHP mantém a aplicação PHP em memória. O framework é inicializado uma vez (bootstrap do container, carregamento de configurações, resolução de rotas), e a partir daí cada request reutiliza esse estado quente.
A mecânica se parece com o que Node.js e Go fazem naturalmente: o processo sobe, carrega a aplicação, e fica escutando. A diferença é que o PHP não foi desenhado para isso. O modelo share-nothing era uma feature de segurança (nenhum request contamina o próximo). O worker mode precisa de cuidados extras para resetar estado entre requests: variáveis globais, sessões e configurações INI são limpos automaticamente pelo FrankenPHP, mas código que depende de singletons mutáveis ou cache em memória estática pode precisar de ajustes.
Para Laravel, a integração é via Octane:
composer require laravel/octane
php artisan octane:install --server=frankenphp
php artisan octane:start
Para Symfony, é via Runtime Component:
composer require runtime/frankenphp-symfony
APP_RUNTIME=Runtime\FrankenPhpSymfony\Runtime php public/index.php
Nos dois casos, o código da aplicação não precisa mudar. O framework cuida de adaptar o ciclo de vida.
Benchmarks: FrankenPHP vs PHP-FPM em números
Vou separar os números por modo de operação, porque a diferença entre classic e worker mode é grande.
Classic mode vs PHP-FPM
O Tideways publicou um benchmark em dezembro de 2025 comparando FrankenPHP classic mode contra Nginx + PHP-FPM. Os resultados: praticamente empate.
- Hello World: PHP-FPM 18.479 req/s vs FrankenPHP 18.404 req/s
- HTML response: PHP-FPM 7.023 req/s vs FrankenPHP 6.934 req/s
- P99 latência (HTML): PHP-FPM 2.02ms vs FrankenPHP 2.06ms
A conclusão deles foi direta: o modo clássico não traz ganho de performance sobre PHP-FPM. As pequenas variações vinham de diferenças de compressão entre Nginx e Caddy, não do PHP em si.
Worker mode vs PHP-FPM
Aqui o cenário muda. Ivan Vulovic publicou uma série de benchmarks com Symfony em uma instância AWS, e os resultados em worker mode foram consistentes:
- Throughput: FrankenPHP ~15.000 req/s vs PHP-FPM ~4.000 req/s (aproximadamente 4x)
- Em carga alta (2.000 req/s simultâneos): latência média do PHP-FPM disparou para quase 1 segundo, enquanto FrankenPHP manteve ~5ms
- Zero requests falhados no FrankenPHP sob carga que causava timeouts no PHP-FPM
A Sylius (plataforma de e-commerce baseada em Symfony) reportou que o worker mode reduziu tempos de resposta em 80% e permitiu servir o mesmo tráfego com 6x menos máquinas.
Esses números fazem sentido quando você entende o que está sendo eliminado. Em uma aplicação Symfony ou Laravel, o bootstrap do framework pode levar 20-50ms. Em worker mode, esse custo é pago uma vez. Se sua aplicação atende 1.000 requests por segundo, são 20-50 segundos de CPU que você deixa de gastar por segundo. A matemática é simples.
Na prática: Laravel Octane e Symfony Runtime
A integração com Laravel via Octane é a mais madura. O Octane já suportava Swoole e RoadRunner antes do FrankenPHP, então o modelo mental já é conhecido: você instala o driver, configura o Caddyfile se quiser customizar, e inicia o server.
Um Caddyfile de produção com Laravel Octane se parece com isso:
{
frankenphp
order php_server before file_server
}
localhost {
root * /app/public
php_server {
worker {
file /app/public/frankenphp-worker.php
watch
}
}
}
A diretiva watch recarrega o worker automaticamente quando arquivos mudam, útil em desenvolvimento.
No Docker, a configuração se resume a:
docker run -v $PWD:/app -p 443:443 dunglas/frankenphp
O container já vem com PHP, Caddy e worker mode prontos. HTTPS automático funciona até em localhost (com certificados auto-assinados para desenvolvimento).
Para Symfony, a integração é nativa a partir do Runtime Component. O pacote runtime/frankenphp-symfony adapta o ciclo de vida do kernel Symfony para rodar em modo persistente. A partir do Symfony 7.4, o suporte é direto, sem pacotes adicionais.
Quando não trocar (e os cuidados com worker mode)
Worker mode não é uma bala de prata. Alguns cenários onde PHP-FPM continua sendo a escolha mais segura:
- Aplicações que dependem de extensões PHP incompatíveis com o worker mode (ex: extensões que mantêm estado global não-resetável)
- Código legado que usa variáveis globais de forma imprevisível
- Ambientes onde o modelo share-nothing é um requisito de segurança (multi-tenant com isolamento rígido entre requests)
A versão 1.11.2 (fevereiro de 2026) corrigiu um vazamento de sessão entre requests em worker mode (GHSA-r3xh-3r3w-47gp). Esse tipo de bug demonstra que o modelo persistente introduz uma classe de problemas que não existe no PHP-FPM. Cada release do FrankenPHP adiciona mais proteções (reset automático de $_SESSION, $_REQUEST e configurações INI). Em produção, configurar max_requests para reiniciar workers periodicamente ajuda a prevenir memory leaks em aplicações com alto volume.
Outra limitação: o modo clássico do FrankenPHP não traz ganho sobre PHP-FPM. Se você não pode usar worker mode por restrições da aplicação, a migração não compensa.
A versão 1.11.3 (25 de fevereiro de 2026) adicionou suporte a PHP 8.5, e a 1.11.2 trouxe ganhos de performance do Go 1.26 (~30% em chamadas CGO, 10-40% no garbage collector). O projeto está em ritmo acelerado de releases.
Conclusão
O FrankenPHP resolve um problema específico e resolve bem: eliminar o overhead de bootstrap em aplicações PHP que rodam frameworks pesados. Se você mantém Laravel ou Symfony em produção com PHP-FPM e cada request gasta 30-50ms só para inicializar o framework, o worker mode pode cortar isso para perto de zero.
A adoção pela PHP Foundation em 2025 tirou o risco de "projeto de uma pessoa só". Com o código sob php/ no GitHub, manutenção compartilhada e hosting nativo em provedores como Laravel Cloud e Clever Cloud, a barreira de entrada para testar caiu bastante.
Para quem quer experimentar sem compromisso: o modo clássico funciona como drop-in replacement. Troque Nginx + PHP-FPM por FrankenPHP classic, valide que tudo funciona, e depois ative worker mode quando estiver confortável. Sem reescrita de código.
Referências pesquisadas nesta publicação
- FrankenPHP Is Now Officially Supported by The PHP Foundation
- Testing if FrankenPHP Classic Mode is faster than PHP-FPM - Tideways
- FrankenPHP v1.11.2 Released - Laravel News
- FrankenPHP 1.11.3 with PHP 8.5 - Clever Cloud Changelog
- Symfony 8: FrankenPHP vs RoadRunner Benchmarked - HackerNoon
- FrankenPHP - GitHub (php/frankenphp)