Manual de Migração

NGINX para quem
usava Apache2

Guia prático de equivalências, sintaxe e conceitos para sysadmins migrando de servidor.

Filosofia: processo vs evento

O Apache2 e o NGINX resolvem o mesmo problema de formas radicalmente diferentes. Entender essa diferença é o primeiro passo para migrar sem dor.

Apache2
Modelo orientado a PROCESSO

Cada requisição = 1 processo
(ou thread, com mpm_worker)

Simples de configurar.
Suporte a .htaccess por pasta.
Módulos carregados em memória.

Gargalo: muitas conexões
simultâneas consomem RAM.
NGINX
Modelo orientado a EVENTO

1 worker = N conexões
(loop de eventos assíncrono)

Config centralizada.
Sem .htaccess (por design).
Módulos compilados no binário.

Eficiente: 10k+ conexões com
baixíssimo uso de memória.
📌 Conceito-chave O NGINX foi criado para ser um proxy reverso de alta performance. Servir arquivos estáticos e fazer proxy para PHP-FPM, Node.js ou qualquer backend é onde ele brilha.

Quando o NGINX ganha do Apache?

Instalação no Ubuntu/Debian

⚠️ Atenção Apache2 e NGINX não podem escutar na porta 80/443 ao mesmo tempo. Pare o Apache antes de iniciar o NGINX.

Parar e desabilitar o Apache2

sudo systemctl stop apache2 && sudo systemctl disable apache2

Instalar o NGINX

sudo apt update && sudo apt install nginx -y

Habilitar e iniciar

sudo systemctl enable nginx && sudo systemctl start nginx

Verificar se está rodando

sudo systemctl status nginx

Acesse http://localhost — você deve ver a página padrão do NGINX.

Estrutura de arquivos

Apache2
/etc/apache2/
  apache2.conf       # config global
  ports.conf         # portas
  sites-available/   # vhosts inativos
  sites-enabled/     # vhosts ativos
  mods-available/    # módulos
  conf-available/    # configs extras
NGINX
/etc/nginx/
  nginx.conf         # config global
  # (sem ports.conf separado)
  sites-available/   # blocos inativos
  sites-enabled/     # blocos ativos
  conf.d/            # configs extras
  # (módulos compilados no bin)

Anatomia do nginx.conf

# Contexto: global
user  www-data;
worker_processes  auto;      # = nº de CPUs

events {
    worker_connections  1024;  # conexões por worker
}

# Contexto: http
http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile      on;
    keepalive_timeout  65;

    # Inclui todos os sites ativos
    include /etc/nginx/sites-enabled/*;
}
💡 Dica A hierarquia de contextos no NGINX é: global → events → http → server → location. Diretivas de um contexto são herdadas pelos filhos, mas podem ser sobrescritas.

Ativar/desativar sites (igual ao Apache)

sudo ln -s /etc/nginx/sites-available/meusite /etc/nginx/sites-enabled/
sudo rm /etc/nginx/sites-enabled/meusite
sudo nginx -t && sudo systemctl reload nginx

Virtual Hosts vs Server Blocks

O que o Apache chama de VirtualHost, o NGINX chama de server block. A lógica é idêntica; a sintaxe muda.

Site estático simples

Apache2 — /etc/apache2/sites-available/meusite.conf
<VirtualHost *:80>
    ServerName  meusite.com
    ServerAlias www.meusite.com
    DocumentRoot /var/www/meusite

    <Directory /var/www/meusite>
        AllowOverride All
        Require all granted
    </Directory>

    ErrorLog ${APACHE_LOG_DIR}/erro.log
    CustomLog ${APACHE_LOG_DIR}/acesso.log combined
</VirtualHost>
NGINX — /etc/nginx/sites-available/meusite
server {
    listen       80;
    server_name  meusite.com www.meusite.com;
    root         /var/www/meusite;
    index        index.html index.php;

    location / {
        try_files $uri $uri/ =404;
    }

    error_log  /var/log/nginx/erro.log;
    access_log /var/log/nginx/acesso.log;
}

Site PHP com PHP-FPM (substitui mod_php)

⚠️ Importante O NGINX não executa PHP nativamente. Ele delega a execução ao PHP-FPM via FastCGI. Instale: sudo apt install php8.3-fpm
Apache2 (com mod_php)
<VirtualHost *:80>
    ServerName app.local
    DocumentRoot /var/www/app/public

    # PHP roda dentro do Apache
    # mod_php cuida disso

    <Directory /var/www/app/public>
        AllowOverride All
        Require all granted
    </Directory>
</VirtualHost>
NGINX + PHP-FPM
server {
    listen      80;
    server_name app.local;
    root        /var/www/app/public;
    index       index.php index.html;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        include         fastcgi_params;
        fastcgi_pass    unix:/run/php/php8.3-fpm.sock;
        fastcgi_param   SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
    }
}

Sem .htaccess — e isso é bom

O NGINX não lê arquivos .htaccess. Isso é intencional: ler e interpretar um arquivo por diretório a cada requisição tem custo de I/O. No NGINX, toda a configuração fica nos server blocks.

🔴 Atenção Arquivos .htaccess do seu projeto serão ignorados silenciosamente. Você precisa traduzir as regras para a sintaxe do NGINX nos server blocks.

Tabela de equivalências .htaccess → NGINX

Apache .htaccess NGINX (location block) Descrição
Options -Indexes autoindex off; Desabilitar listagem de diretório
Options +Indexes autoindex on; Habilitar listagem de diretório
DirectoryIndex index Arquivo padrão do diretório
ErrorDocument 404 error_page 404 Página de erro customizada
AllowOverride All N/A Conceito não existe no NGINX
Require all denied deny all; Bloquear acesso ao diretório
Require ip 192.168 allow 192.168.0.0/16; deny all; Controle de acesso por IP
Header set X-Frame add_header X-Frame-Options Cabeçalhos HTTP customizados
ExpiresActive expires / add_header Cache-Control Cache de arquivos estáticos

Cache de estáticos — exemplo completo

Apache2 .htaccess
ExpiresActive On
ExpiresByType image/png  "access plus 1 month"
ExpiresByType text/css  "access plus 1 week"
ExpiresByType application/javascript "access plus 1 week"
NGINX server block
location ~* \.(png|jpg|gif|ico|webp)$ {
    expires 30d;
    add_header Cache-Control "public";
}

location ~* \.(css|js)$ {
    expires 7d;
    add_header Cache-Control "public";
}

Rewrites: mod_rewrite → try_files / rewrite

A maioria dos RewriteRule do Apache se traduz em try_files ou rewrite no NGINX — geralmente com menos linhas.

Front controller (Laravel, Symfony, CodeIgniter…)

Apache2 .htaccess
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^ index.php [L]
NGINX location block
location / {
    try_files $uri $uri/ /index.php?$query_string;
}
💡 try_files try_files $uri $uri/ /index.php?$query_string significa: tenta o arquivo → tenta como diretório → redireciona para index.php mantendo a query string.

Redirect 301 de domínio

Apache2
RewriteEngine On
RewriteCond %{HTTP_HOST} ^www\. [NC]
RewriteRule ^(.*)$ https://meusite.com/$1 [R=301,L]
NGINX
server {
    listen 80;
    server_name www.meusite.com;
    return 301 https://meusite.com$request_uri;
}

Redirect HTTP → HTTPS

Apache2
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}/$1 [R=301,L]
NGINX
server {
    listen 80;
    server_name meusite.com www.meusite.com;
    return 301 https://$host$request_uri;
}

Rewrite com regex (quando try_files não basta)

location /old-path/ {
    rewrite ^/old-path/(.*)$ /new-path/$1 permanent;
}

# Captura grupos com $1, $2... igual ao Apache
# "permanent" = 301, "redirect" = 302

Proxy Reverso

O NGINX foi feito para isso. Enquanto no Apache você precisa do mod_proxy, no NGINX é nativo e muito mais performático.

Proxy para Node.js / Python / qualquer app na porta local

Apache2 (mod_proxy)
ProxyPreserveHost On
ProxyPass        /  http://127.0.0.1:3000/
ProxyPassReverse /  http://127.0.0.1:3000/
NGINX
location / {
    proxy_pass         http://127.0.0.1:3000;
    proxy_http_version 1.1;
    proxy_set_header   Upgrade $http_upgrade;
    proxy_set_header   Connection 'upgrade';
    proxy_set_header   Host $host;
    proxy_cache_bypass $http_upgrade;
}

Load Balancer simples

upstream backend {
    server 127.0.0.1:3001;
    server 127.0.0.1:3002;
    server 127.0.0.1:3003;
}

server {
    listen 80;
    server_name meusite.com;

    location / {
        proxy_pass http://backend;  # round-robin por padrão
    }
}
💡 Algoritmos de balanceamento Adicione ao bloco upstream: least_conn; (menos conexões) ou ip_hash; (sticky session por IP). Round-robin é o padrão sem declaração.

SSL / TLS com Certbot

Instalar Certbot para NGINX

sudo apt install certbot python3-certbot-nginx -y
sudo certbot --nginx -d meusite.com -d www.meusite.com

O Certbot modifica automaticamente seu server block. O resultado será algo assim:

server {
    listen 80;
    server_name meusite.com www.meusite.com;
    return 301 https://$host$request_uri;  # redirect automático
}

server {
    listen 443 ssl http2;
    server_name meusite.com www.meusite.com;
    root   /var/www/meusite;
    index  index.php index.html;

    # Gerenciado pelo Certbot:
    ssl_certificate     /etc/letsencrypt/live/meusite.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/meusite.com/privkey.pem;
    include             /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam         /etc/letsencrypt/ssl-dhparams.pem;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        include       fastcgi_params;
        fastcgi_pass  unix:/run/php/php8.3-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
    }
}

Renovação automática

sudo certbot renew --dry-run

O Certbot já instala um cron/timer systemd. Apenas verifique que está ativo.

Comandos essenciais

Apache2 NGINX O que faz
apachectl -t nginx -t Testar configuração sem reiniciar
apachectl graceful nginx -s reload Recarregar sem derrubar conexões
service apache2 restart systemctl restart nginx Reiniciar o servidor
service apache2 stop systemctl stop nginx Parar o servidor
a2ensite meusite ln -s sites-available/meusite sites-enabled/ Ativar site
a2dissite meusite rm sites-enabled/meusite Desativar site
a2enmod rewrite Compilado Módulos já estão no binário
tail -f /var/log/apache2/error.log tail -f /var/log/nginx/error.log Ver log de erros em tempo real
tail -f /var/log/apache2/access.log tail -f /var/log/nginx/access.log Ver log de acessos em tempo real

Fluxo de trabalho recomendado

# 1. Edite sua config
sudo nano /etc/nginx/sites-available/meusite

# 2. SEMPRE teste antes de aplicar
sudo nginx -t

# 3. Recarregue sem downtime
sudo systemctl reload nginx

# 4. Veja se deu erro
sudo journalctl -u nginx -f

Tabela geral de equivalências

Conceito Apache2 NGINX Disponível?
Configuração
Config global apache2.conf nginx.conf OK
Config por diretório .htaccess Não existe Traduzir
VirtualHost <VirtualHost> server { } OK
PHP
Executar PHP mod_php (embutido) PHP-FPM (externo) OK
Socket PHP automático fastcgi_pass unix:/run/php/phpX.X-fpm.sock OK
Módulos
mod_rewrite RewriteRule try_files / rewrite OK
mod_proxy ProxyPass proxy_pass OK
mod_ssl SSLEngine on listen 443 ssl OK
mod_deflate AddOutputFilterByType DEFLATE gzip on; gzip_types … OK
mod_headers Header set X-… add_header X-… OK
mod_expires ExpiresActive On expires 30d OK
mod_status /server-status stub_status OK
mod_userdir UserDir public_html Manual Configurar

Compressão GZIP

Apache2
AddOutputFilterByType DEFLATE
    text/html text/css
    application/javascript
NGINX (em http { })
gzip              on;
gzip_vary         on;
gzip_types        text/html text/css
                  application/javascript
                  application/json;
gzip_comp_level   5;

Bloquear acesso a arquivos sensíveis

Apache2 .htaccess
<FilesMatch "^\.env$|\.sqlite$">
    Require all denied
</FilesMatch>
NGINX server block
location ~ /\.(env|sqlite|git) {
    deny all;
    return 404;
}
✅ Checklist de migração
  • Instalar PHP-FPM (php8.x-fpm) e configurar o socket
  • Traduzir todos os .htaccess para server blocks
  • Substituir RewriteRule por try_files ou rewrite
  • Recriar redirects 301/302 como blocos server { return … }
  • Ajustar permissões: usuário www-data precisa ler os arquivos
  • Rodar nginx -t antes de qualquer reload
  • Monitorar /var/log/nginx/error.log nos primeiros dias