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.conf do Debian/Ubuntu já inclui try_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 repita try_files nesse bloco: o Nginx recusa a diretiva duplicada.
  • O bloco que nega arquivos iniciados por ponto protege .env, .git e .htaccess, mas mantém /.well-known acessível para o Let's Encrypt.
  • client_max_body_size precisa estar de acordo com o upload_max_filesize do 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

  1. 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
  2. Acesse http://exemplo.com.br/teste.php. Deve aparecer a versão do PHP e o usuário exemplo.
  3. Apague o arquivo: rm /srv/exemplo/public/teste.php. Não deixe phpinfo() exposto em produção.
  4. Confira o pool: systemctl status php8.4-fpm deve listar processos pool 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á em sites-enabled e se não há outro server block respondendo pelo mesmo nome (nginx -T | grep server_name).
  • 502 Bad Gateway: o socket informado em fastcgi_pass não existe ou tem permissão errada. Compare com ls -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 root apontando para o diretório errado ou open_basedir restritivo demais.
  • Log com "server reached pm.max_children": faltam processos. Aumente pm.max_children se 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.

Esta resposta lhe foi útil? 0 Usuários acharam útil (0 Votos)

Leia também