Instale Nginx com PHP-FPM no Debian 13 ou Ubuntu 24.04 com um usuário e um pool por site, server block seguro e pm.max_children bem dimensionado.
Para que serve
Nginx com PHP-FPM é a combinação mais usada hoje para sites em PHP (WordPress, Laravel, sistemas próprios) sem painel de controle. O Nginx atende as conexões e entrega arquivos estáticos; o PHP-FPM mantém um conjunto de processos PHP que executam os scripts. Este guia monta essa pilha em Debian 13 ou Ubuntu 24.04, com um pool PHP por site e as configurações que evitam os problemas mais comuns.
Pré-requisitos
- VM ou servidor com Debian 13 (PHP 8.4 no repositório) ou Ubuntu 24.04 (PHP 8.3), acesso root.
- Domínio apontando (registro A/AAAA) para o IP do servidor.
- Portas 80 e 443 liberadas no firewall.
Nos exemplos, troque 8.4 por 8.3 se estiver no Ubuntu 24.04, e exemplo.com.br pelo seu domínio.
Passo a passo: instalar Nginx com PHP-FPM
1. Instale os pacotes
apt update
apt install -y nginx php-fpm php-cli php-mysql php-curl php-gd php-mbstring php-xml php-zip php-intl php-opcache
systemctl enable --now nginx php8.4-fpm
2. Crie um usuário e um diretório para o site
Rodar cada site com seu próprio usuário limita o estrago caso um deles seja invadido.
useradd -m -d /srv/exemplo -s /usr/sbin/nologin exemplo
mkdir -p /srv/exemplo/public /srv/exemplo/logs
chown -R exemplo:exemplo /srv/exemplo
chmod 750 /srv/exemplo
usermod -aG exemplo www-data
O último comando permite que o Nginx (usuário www-data) leia os arquivos estáticos do site.
3. Crie um pool PHP-FPM dedicado
Crie /etc/php/8.4/fpm/pool.d/exemplo.conf:
[exemplo]
user = exemplo
group = exemplo
listen = /run/php/exemplo.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
pm = dynamic
pm.max_children = 20
pm.start_servers = 4
pm.min_spare_servers = 2
pm.max_spare_servers = 6
pm.max_requests = 500
request_terminate_timeout = 120s
php_admin_value[error_log] = /srv/exemplo/logs/php-error.log
php_admin_flag[log_errors] = on
php_admin_value[open_basedir] = /srv/exemplo:/tmp:/usr/share/php
Como dimensionar pm.max_children: descubra quanto cada processo PHP consome e divida a memória que você quer reservar ao site por esse valor. Com o site em uso:
ps -C php-fpm8.4 -o rss= | awk '{s+=$1; n++} END {print s/n/1024 " MB por processo"}'
Se cada processo usa 60 MB e você quer reservar 2 GB ao site, pm.max_children fica em torno de 30. Para sites de muito tráfego e servidor dedicado ao site, pm = static elimina o custo de criar processos sob demanda; para muitos sites pequenos, pm = ondemand economiza memória.
Aplique:
php-fpm8.4 -t && systemctl reload php8.4-fpm
4. Crie o server block do Nginx
Crie /etc/nginx/sites-available/exemplo.conf:
server {
listen 80;
listen [::]:80;
server_name exemplo.com.br www.exemplo.com.br;
root /srv/exemplo/public;
index index.php index.html;
access_log /srv/exemplo/logs/access.log;
error_log /srv/exemplo/logs/error.log;
client_max_body_size 64m;
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/exemplo.sock;
fastcgi_read_timeout 120s;
}
location ~* \.(css|js|jpg|jpeg|png|gif|webp|svg|ico|woff2?)$ {
expires 30d;
access_log off;
}
location ~ /\.(?!well-known) {
deny all;
}
}
Pontos importantes:
- O
snippets/fastcgi-php.confdo Debian/Ubuntu já incluitry_files $fastcgi_script_name =404;, que impede o Nginx de repassar ao PHP arquivos que não existem (brecha antiga explorada com uploads de imagem contendo código). Por isso não repitatry_filesnesse bloco: o Nginx recusa a diretiva duplicada. - O bloco que nega arquivos iniciados por ponto protege
.env,.gite.htaccess, mas mantém/.well-knownacessível para o Let's Encrypt. client_max_body_sizeprecisa estar de acordo com oupload_max_filesizedo PHP.
Ative o site e remova o site padrão:
ln -s /etc/nginx/sites-available/exemplo.conf /etc/nginx/sites-enabled/
rm -f /etc/nginx/sites-enabled/default
nginx -t && systemctl reload nginx
5. Ajustes gerais do Nginx
Em /etc/nginx/nginx.conf, dentro do bloco http, o arquivo padrão já traz estas linhas, algumas comentadas com #. Descomente em vez de acrescentar de novo, porque diretivas repetidas no mesmo bloco fazem o nginx -t falhar:
server_tokens off; # não exibe a versão do Nginx nas páginas de erro
gzip on;
gzip_vary on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml text/javascript;
O worker_processes auto; já vem por padrão e usa um processo por núcleo, o que é adequado para os Xeon dos nossos planos.
Para filtrar ataques comuns (injeção de SQL, XSS, varredura de arquivos) antes que cheguem ao PHP, veja também o artigo «Como instalar um WAF no servidor: ModSecurity com OWASP CRS e Cloudflare».
6. Ative HTTPS
Com o site respondendo em HTTP, emita o certificado seguindo o artigo sobre Let's Encrypt com Certbot desta base. O Certbot ajusta o server block para a porta 443 automaticamente. Depois, para deixar só protocolos e cifras seguros, veja também o artigo «Como desabilitar TLS 1.0 e 1.1 e tirar nota A no SSL Labs (Nginx, Apache e Windows Server)».
Como saber se funcionou
- Crie um teste temporário:
echo '<?php echo "PHP " . PHP_VERSION . " executando como " . posix_getpwuid(posix_geteuid())["name"];' > /srv/exemplo/public/teste.php chown exemplo:exemplo /srv/exemplo/public/teste.php - Acesse
http://exemplo.com.br/teste.php. Deve aparecer a versão do PHP e o usuárioexemplo. - Apague o arquivo:
rm /srv/exemplo/public/teste.php. Não deixephpinfo()exposto em produção. - Confira o pool:
systemctl status php8.4-fpmdeve listar processospool exemplo.
Problemas comuns
- Navegador baixa o arquivo .php em vez de executar: o bloco
location ~ \.php$não está sendo usado. Verifique se o arquivo está emsites-enablede se não há outro server block respondendo pelo mesmo nome (nginx -T | grep server_name). - 502 Bad Gateway: o socket informado em
fastcgi_passnão existe ou tem permissão errada. Compare comls -l /run/php/. Há um artigo inteiro sobre 502/503/504 nesta base. - "File not found." em texto puro: o PHP-FPM não consegue ler o arquivo, geralmente por
rootapontando para o diretório errado ouopen_basedirrestritivo demais. - Log com "server reached pm.max_children": faltam processos. Aumente
pm.max_childrense houver memória livre, ou investigue scripts lentos.
Perguntas frequentes
Qual a diferença entre PHP-FPM e mod_php?
O mod_php roda o PHP dentro de cada processo do Apache. O PHP-FPM é um serviço separado, com seus próprios processos, que o Nginx (ou o Apache) chama por FastCGI. Ele permite um pool e um usuário por site e gasta menos memória com arquivos estáticos.
Qual valor usar em pm.max_children?
Divida a memória que você quer reservar ao site pelo consumo médio de cada processo PHP. Com processos de 60 MB e 2 GB reservados, o valor fica em torno de 30.
Devo usar pm dynamic, static ou ondemand?
dynamic serve para a maioria dos casos. static é melhor para um site de muito tráfego com o servidor dedicado a ele. ondemand economiza memória quando há muitos sites pequenos.
Por que o navegador baixa o arquivo .php em vez de executar?
O Nginx não está usando o bloco location ~ \.php$ do site. Confira se o arquivo está em sites-enabled e se outro server block não responde pelo mesmo domínio.
Leitura complementar
Precisa de ajuda?
Se ficar com alguma dúvida, abra um ticket na área do cliente ou fale com o suporte pelo WhatsApp.
