Dia 15: Apache e Nginx — Web Server e Virtual Hosts
Apache e Nginx são os dois web servers mais utilizados em Linux, com arquitecturas diferentes que se complementam. Apache usa um modelo baseado em processos ou threads — cada ligação ocupa um processo ou thread dedicado. Nginx usa um modelo assíncrono orientado a eventos — um único processo gere milhares de ligações simultâneas. A combinação mais comum em produção é Nginx como reverse proxy à frente de Apache: Nginx serve conteúdo estático e faz load balancing, Apache processa PHP e conteúdo dinâmico. Este artigo mostra como instalar ambos, configurar virtual hosts, montar um reverse proxy, activar HTTPS com Let’s Encrypt e implementar load balancing.
Atenção: Nunca ter Apache e Nginx a escutar na mesma porta (80 ou 443) em simultâneo. Conflitos de portas causam falhas de arranque silenciosas — um serviço arranca e o outro fica parado sem erro evidente.
Neste artigo
Apache vs Nginx — Quando Usar Cada Um
A escolha entre Apache e Nginx depende do tipo de conteúdo a servir. Apache é mais antigo e tem suporte nativo para .htaccess, directivas por directório e mod_php. Nginx é mais rápido a servir ficheiros estáticos (imagens, CSS, JS) e foi desenhado desde o início para funcionar como reverse proxy e load balancer. A documentação oficial de ambos cobre estes cenários em detalhe (Apache HTTP Server Docs, Nginx Beginner’s Guide).
O modelo de processamento explica a diferença de performance. No Apache, o MPM (Multi-Processing Module) prefork cria um processo por ligação — eficaz mas pesado em memória. O MPM worker usa threads, mais leve. O Nginx usa um modelo event-driven: um processo master gere processos worker, e cada worker processa ligações de forma assíncrona via epoll no Linux. Isto permite a um único worker gerir centenas de ligações sem bloquear.
| Característica | Apache | Nginx |
|---|---|---|
| Modelo | Processo/Thread por ligação | Assíncrono, orientado a eventos |
| Conteúdo estático | Rápido | Mais rápido (até 2-3x) |
| Conteúdo dinâmico (PHP) | Nativo (mod_php) | Via PHP-FPM (externo) |
| .htaccess | Suportado | Não suportado |
| Reverse Proxy | Via mod_proxy | Nativo e optimizado |
| Load Balancing | Via mod_proxy_balancer | Nativo (upstream) |
| Configuração por directório | Sim (.htaccess) | Não (apenas config global) |
Em produção, o padrão mais comum é Nginx à frente: recebe as ligações na porta 80/443, serve ficheiros estáticos directamente e faz proxy do conteúdo dinâmico para Apache (ou PHP-FPM) num backend. Esta arquitectura tira o melhor dos dois — velocidade de Nginx para estático, flexibilidade de Apache para dinâmico.
Pré-requisitos e Instalação
Antes de instalar, confirmar que a porta 80 está livre. Se já existir um web server activo, há que pará-lo primeiro ou usar uma porta diferente. O comando ss mostra o que está a escutar (coberto em detalhe no Dia 8 deste curso).
O comando seguinte verifica se a porta 80 está ocupada antes de instalar. Se retornar resultado, algo já está a escutar e há que resolver o conflito antes de avançar.
ss -tlnp | grep ':80 '
A instalação difere entre Debian/Ubuntu e RHEL/Rocky/Alma. No Debian, o pacote chama-se apache2 e o binário é apache2. No RHEL, o pacote chama-se httpd e o binário é httpd. Para Nginx, o nome é igual em ambas as famílias.
# Ubuntu / Debian
sudo apt update
sudo apt install apache2 nginx
# RHEL / Rocky / AlmaLinux
sudo dnf install httpd nginx
Depois de instalar, activar e iniciar os serviços. O enable garante que o serviço arranca automaticamente após reinício. O systemctl foi coberto em detalhe no Dia 7 deste curso.
# Ubuntu / Debian — Apache
sudo systemctl enable apache2
sudo systemctl start apache2
# RHEL / Rocky / AlmaLinux — Apache
sudo systemctl enable httpd
sudo systemctl start httpd
# Nginx (ambas as distribuições)
sudo systemctl enable nginx
sudo systemctl start nginx
A firewall tem de permitir tráfego HTTP (porta 80) e HTTPS (porta 443). No Ubuntu usa-se UFW; no RHEL usa-se firewalld. Ambos foram abordados no Dia 9.
# Ubuntu / Debian — UFW
sudo ufw allow 'Apache Full'
sudo ufw allow 'Nginx Full'
# RHEL / Rocky / AlmaLinux — firewalld
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload
Neste ponto, abrir o navegador em http://IP-do-servidor deve mostrar a página predefinida do Apache ou do Nginx (conforme o que arrancou primeiro na porta 80).
Passo 1 — Virtual Hosts no Apache
Virtual hosts permitem servir múltiplos sites a partir do mesmo servidor Apache, distinguindo-os pelo nome de domínio (name-based virtual hosting). Cada site tem a sua própria configuração, directório raiz e logs. A documentação oficial está em Apache Virtual Hosts.
No Debian/Ubuntu, os ficheiros de configuração ficam em /etc/apache2/sites-available/ e activam-se com a2ensite. No RHEL, ficam em /etc/httpd/conf.d/ e ficam activos automaticamente (não há a2ensite). Começar por criar o directório raiz do site.
sudo mkdir -p /var/www/exemplo.pt/public_html
echo "<h1>Site exemplo.pt a funcionar</h1>" | sudo tee /var/www/exemplo.pt/public_html/index.html
sudo chown -R www-data:www-data /var/www/exemplo.pt/
www-data é o utilizador do Apache no Debian. No RHEL, o utilizador é apache — ajustar o chown conforme a distribuição.
Agora criar o ficheiro de virtual host. No Debian/Ubuntu, criar em /etc/apache2/sites-available/exemplo.pt.conf:
<VirtualHost *:80>
ServerName exemplo.pt
ServerAlias www.exemplo.pt
DocumentRoot /var/www/exemplo.pt/public_html
<Directory /var/www/exemplo.pt/public_html>
Options -Indexes +FollowSymLinks
AllowOverride All
Require all granted
</Directory>
ErrorLog ${APACHE_LOG_DIR}/exemplo_error.log
CustomLog ${APACHE_LOG_DIR}/exemplo_access.log combined
</VirtualHost>
Cada directiva tem um propósito específico: ServerName é o domínio principal, ServerAlias permite domínios alternativos, DocumentRoot define o directório base, AllowOverride All permite .htaccess, e Options -Indexes impede a listagem de directórios (segurança).
Activar o site e recarregar a configuração. No Debian, usar a2ensite; no RHEL, basta colocar o ficheiro em conf.d/ e recarregar.
# Ubuntu / Debian
sudo a2ensite exemplo.pt.conf
sudo apache2ctl configtest
sudo systemctl reload apache2
# RHEL / Rocky / AlmaLinux
# (colocar o .conf em /etc/httpd/conf.d/ e usar httpd nas variaveis de log)
sudo httpd -t
sudo systemctl reload httpd
O configtest (ou -t) valida a sintaxe antes de recarregar. Se retornar Syntax OK, é seguro recarregar. Se houver erro, corrigir antes de recarregar — um reload com configuração inválida pode deixar o Apache parado.
Passo 2 — Server Blocks no Nginx
No Nginx, o equivalente aos virtual hosts do Apache chama-se server blocks. A configuração de cada site é um bloco server dentro de um ficheiro .conf. No Debian/Ubuntu, os ficheiros vão em /etc/nginx/sites-available/ (com symlink para sites-enabled/); no RHEL e outras distribuições, vão em /etc/nginx/conf.d/. A documentação oficial está em nginx server_names e ArchWiki Nginx.
Criar o directório raiz (igual ao Apache, mas o utilizador do Nginx é nginx ou www-data conforme a distribuição):
sudo mkdir -p /var/www/exemplo.pt/html
echo "<h1>Nginx — exemplo.pt</h1>" | sudo tee /var/www/exemplo.pt/html/index.html
sudo chown -R www-data:www-data /var/www/exemplo.pt/
Criar o ficheiro de configuração do server block. No Debian/Ubuntu, colocar em /etc/nginx/sites-available/exemplo.pt e criar symlink para sites-enabled. No RHEL, colocar em /etc/nginx/conf.d/exemplo.pt.conf.
server {
listen 80;
server_name exemplo.pt www.exemplo.pt;
root /var/www/exemplo.pt/html;
index index.html index.htm;
location / {
try_files $uri $uri/ =404;
}
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
expires 30d;
add_header Cache-Control "public, no-transform";
}
error_page 404 /404.html;
access_log /var/log/nginx/exemplo_access.log;
error_log /var/log/nginx/exemplo_error.log;
}
As directivas principais: listen 80 define a porta, server_name responde pelos domínios, root é o directório base, try_files tenta servir o ficheiro pedido, depois um directório, e se não existir devolve 404. O bloco location ~* activa cache de 30 dias para ficheiros estáticos.
Activar e validar a configuração. O nginx -t testa a sintaxe — deve retornar test is successful.
# Ubuntu / Debian — criar symlink e activar
sudo ln -s /etc/nginx/sites-available/exemplo.pt /etc/nginx/sites-enabled/
# Validar e recarregar (todas as distribuicoes)
sudo nginx -t
sudo systemctl reload nginx
Nota: No Debian/Ubuntu, o ficheiro predefinido /etc/nginx/sites-enabled/default pode capturar todos os pedidos se for o primeiro a ser carregado. Se o site não responder, remover o default com sudo rm /etc/nginx/sites-enabled/default e recarregar.
Passo 3 — Reverse Proxy com Nginx
Um reverse proxy recebe pedidos HTTP no front-end e encaminha-os para um servidor back-end. O caso mais típico é Nginx na porta 80 a fazer proxy para Apache (ou uma aplicação Node.js, Python, etc.) numa porta interna. O Nginx é excelente nisto porque foi desenhado para gerir ligações upstream de forma eficiente. A documentação oficial do módulo de proxy está em ngx_http_proxy_module.
Primeiro, configurar o Apache para escutar na porta 8080 em vez da 80. Editar /etc/apache2/ports.conf (Debian) ou /etc/httpd/conf/httpd.conf (RHEL) e alterar a porta:
# Em ports.conf (Debian) ou httpd.conf (RHEL)
Listen 8080
Depois actualizar o virtual host do Apache para escutar na 8080. Alterar a primeira linha do ficheiro criado no Passo 1:
<VirtualHost *:8080>
# ... resto igual ao Passo 1
</VirtualHost>
Agora configurar o Nginx como reverse proxy. O Nginx vai receber na porta 80 e encaminhar para Apache na 8080. Os cabeçalhos proxy_set_header são essenciais: sem eles, o Apache back-end não sabe o IP real do cliente nem o host pedido.
server {
listen 80;
server_name exemplo.pt www.exemplo.pt;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# Servir ficheiros estaticos directamente (sem proxy)
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff2?)$ {
root /var/www/exemplo.pt/public_html;
expires 30d;
}
}
Os cabeçalhos têm funções específicas: Host preserva o domínio pedido, X-Real-IP passa o IP real do visitante (sem isto, o Apache só vê 127.0.0.1), X-Forwarded-For mantém a cadeia de proxies, e X-Forwarded-Proto indica se a ligação original era HTTP ou HTTPS.
Para suporte a WebSockets (aplicações em tempo real, chat, etc.), adicionar estes cabeçalhos dentro do bloco location / — sem eles, o upgrade de protocolo falha e as ligações WebSocket são terminadas. Mais detalhes em Nginx WebSocket.
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
Validar e aplicar:
sudo nginx -t
sudo systemctl reload nginx
sudo systemctl reload apache2 # ou httpd no RHEL
Passo 4 — HTTPS com Let’s Encrypt
Let’s Encrypt emite certificados TLS gratuitos e válidos, com renovação automática. O Certbot é o cliente recomendado — configura o certificado e actualiza o virtual host automaticamente. O domínio tem de apontar para o servidor (registo A no DNS) antes de executar o Certbot, senão a validação falha.
Instalar o Certbot. No Debian/Ubuntu, o pacote inclui plugins específicos para Apache e Nginx que automatizam a configuração do HTTPS.
# Ubuntu / Debian
sudo apt install certbot python3-certbot-apache python3-certbot-nginx
# RHEL / Rocky / AlmaLinux
sudo dnf install certbot python3-certbot-apache python3-certbot-nginx
Para Apache, executar o Certbot com a flag --apache. Para Nginx, usar --nginx. O Certbot detecta os virtual hosts configurados, pede o endereço de email para notificações de expiração, e modifica automaticamente a configuração para usar HTTPS. Mais detalhes em Nginx HTTPS.
# Apache
sudo certbot --apache -d exemplo.pt -d www.exemplo.pt
# Nginx
sudo certbot --nginx -d exemplo.pt -d www.exemplo.pt
O Certbot instala um timer do systemd que renova automaticamente os certificados antes de expirarem (validade de 90 dias). Verificar se o timer está activo:
sudo systemctl list-timers certbot.timer
Para testar a renovação sem esperar, executar sudo certbot renew --dry-run. Se não houver erros, a renovação automática está configurada correctamente.
Passo 5 — Load Balancing com Nginx
O Nginx pode distribuir tráfego entre múltiplos backends usando o bloco upstream. Isto aumenta a disponibilidade e permite escalar horizontalmente — adicionar mais servidores back-end conforme o tráfego cresce. A documentação oficial está em Nginx Load Balancing.
O bloco upstream define o conjunto de servidores back-end. Por defeito, o Nginx usa round-robin (distribuição cíclica). Outros métodos disponíveis: least_conn (menos ligações activas) e ip_hash (mesmo IP vai sempre ao mesmo backend — útil para sessões).
upstream backend_aplicacao {
# Round-robin (predefinido)
server 192.168.1.10:8080;
server 192.168.1.11:8080;
server 192.168.1.12:8080;
}
# Alternativa: least_conn
# upstream backend_aplicacao {
# least_conn;
# server 192.168.1.10:8080;
# server 192.168.1.11:8080;
# }
# Alternativa: ip_hash (sessoes sticky)
# upstream backend_aplicacao {
# ip_hash;
# server 192.168.1.10:8080;
# server 192.168.1.11:8080;
# }
server {
listen 80;
server_name app.exemplo.pt;
location / {
proxy_pass http://backend_aplicacao;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
Para controlar o comportamento quando um backend falha, adicionar parâmetros a cada servidor: max_fails=3 marca o servidor como indisponível após 3 falhas, fail_timeout=30s define o tempo de espera antes de tentar novamente, e weight=N dá mais peso a servidores mais potentes (recebem mais pedidos).
upstream backend_aplicacao {
server 192.168.1.10:8080 weight=3 max_fails=3 fail_timeout=30s;
server 192.168.1.11:8080 weight=1 max_fails=3 fail_timeout=30s;
server 192.168.1.12:8080 backup; # so usa se os outros falharem
}
Validar e aplicar:
sudo nginx -t
sudo systemctl reload nginx
Erros Comuns
Os erros mais frequentes ao configurar Apache e Nginx, com causa e solução directa:
| Problema | Causa | Solução |
|---|---|---|
| “(98) Address already in use” no arranque | Outro processo ja escuta na porta 80 ou 443 | Executar ss -tlnp | grep ':80' para identificar o processo e para-lo, ou mudar a porta |
| 403 Forbidden | Permissoes incorrectas no directorio raiz ou falta de index | Verificar chown e chmod 755 no directorio. Confirmar que existe index.html |
| 502 Bad Gateway | Backend (Apache/app) nao esta a correr ou a porta do proxy_pass esta errada | Verificar systemctl status apache2. Confirmar que a porta no proxy_pass corresponde a do backend |
| SSL_ERROR_RX_RECORD_TOO_LONG | Aceder a HTTPS numa porta que serve HTTP (ou vice-versa) | Confirmar que listen 443 ssl esta configurado e que o certificado esta instalado |
| .htaccess ignorado | Nginx nao suporta .htaccess; no Apache, AllowOverride pode estar None | No Apache: mudar para AllowOverride All. No Nginx: converter regras para a config do Nginx |
| “conflicting server name” no log | Dois server blocks com o mesmo server_name | Remover duplicados. O Nginx usa o primeiro que encontra, ignorando o resto |
| Site carrega sem CSS/imagens | Caminho root errado ou ficheiros estaticos a passar pelo proxy | Verificar directiva root. Adicionar bloco location para estaticos no Nginx |
Checklist de Verificação
Antes de colocar o servidor em produção, verificar cada ponto desta lista. Estes passos evitam os erros mais comuns e garantem que a configuração está consistente.
- Testar a sintaxe: executar
sudo nginx -tesudo apache2ctl configtest(ouhttpd -t) antes de cadareload. - Verificar portas: confirmar que nao ha conflito com
ss -tlnp | grep -E ':80|:443|:8080'. Apenas um servico por porta. - Testar permissões:
ls -la /var/www/exemplo.pt/— o utilizador do web server deve ser dono dos ficheiros. - Confirmar DNS: o dominio tem de apontar para o IP do servidor. Testar com
dig exemplo.ptounslookup exemplo.pt. - Verificar HTTPS: aceder a
https://exemplo.ptno navegador e confirmar que o cadeado aparece sem avisos. Usar SSL Labs para um teste completo. - Confirmar renovação automatica: executar
sudo certbot renew --dry-rune verificarsystemctl list-timers certbot.timer. - Verificar logs:
tail -f /var/log/nginx/error.logetail -f /var/log/apache2/exemplo_error.log— nao devem ter erros 502, 503 ou 403.
Artigos Relacionados
- Dia 7 — Systemd: Services, Timers e journalctl — O sistema de gestão de serviços usado para iniciar, parar e recarregar Apache e Nginx.
- Dia 8 — Redes Linux: ip, ss, nmcli e DNS — Comando
sspara diagnosticar portas em uso e conflitos. - Dia 9 — Firewalls: iptables/nftables vs UFW vs firewalld — Regras de firewall para abrir portas 80 e 443 com segurança.
- Dia 10 — SSH: Configuração, Chaves e Hardening — Acesso remoto seguro para administrar o servidor web.
- Dia 13 — Cron e Agendamento: crontab, at e systemd timers — O timer do Certbot usa systemd para renovação automática de certificados.