O erro 403 Forbidden no Nginx tem três causas dominantes: a diretiva try_files ausente ou incorreta, permissões que impedem o processo do Nginx de ler os arquivos, e — em servidores CentOS, AlmaLinux ou Rocky — o SELinux bloqueando o acesso ao diretório. O que praticamente nunca é a causa: o .htaccess, porque o Nginx simplesmente não lê esse arquivo.
Essa última diferença é o que mais confunde quem vem do Apache. Boa parte dos tutoriais de erro 403 manda renomear o .htaccess — no Nginx, isso não muda absolutamente nada. Este guia cobre o diagnóstico específico do Nginx, do log até a configuração.
Comece pelo log — ele diz a causa em uma linha:
sudo tail -30 /var/log/nginx/error.log
| directory index of … is forbidden | Falta try_files ou arquivo índice |
| Permission denied | Permissões de arquivo ou pasta |
| access forbidden by rule | Uma diretiva deny na configuração |
| Nada no log do Nginx | Provavelmente SELinux — veja o log de auditoria |
Conteúdo
Por que o Nginx é diferente do Apache aqui
Vale começar por isso, porque poupa horas: o Nginx não usa .htaccess. Não existe arquivo de configuração por diretório. Toda a configuração fica centralizada, normalmente em /etc/nginx/nginx.conf e nos arquivos dentro de /etc/nginx/sites-available/ ou /etc/nginx/conf.d/.
Isso significa que:
- Renomear ou apagar o
.htaccessnão tem efeito nenhum. Se o arquivo existir na pasta do site, o Nginx simplesmente o ignora — ele é apenas um arquivo comum ali. - Regras de bloqueio que você procuraria no
.htaccessestão nos blocosserverelocationda configuração. - Qualquer alteração exige recarregar o Nginx para valer, ao contrário do Apache, que lê o
.htaccessa cada requisição.
E há um detalhe que confunde muita gente na página de erro:
A página de erro do Nginx costuma mostrar algo como nginx/1.18.0 (Ubuntu) logo abaixo do “403 Forbidden”. Esse número é apenas a versão do servidor — ele não tem relação com o motivo do bloqueio. Pesquisar pela versão não ajuda: a causa e a solução são as mesmas em todas elas.
Comece pelo log
O log do Nginx diz a causa direto, e economiza todo o processo de tentativa e erro:
# Últimas entradas do log de erro
sudo tail -30 /var/log/nginx/error.log
# Filtrar só o que interessa
sudo grep -i "forbidden\|permission denied" /var/log/nginx/error.log | tail -20
# Acompanhar em tempo real enquanto testa
sudo tail -f /var/log/nginx/error.log
Deixe o tail -f rodando e recarregue a página no navegador — a linha que aparecer no momento do erro é a sua resposta. As mensagens mais comuns e o que cada uma indica:
| Mensagem no log | Causa |
|---|---|
directory index of "/var/www/..." is forbidden | Não há arquivo índice e a listagem de diretório está desativada. Falta try_files ou index |
open() "/var/www/..." failed (13: Permission denied) | O usuário do Nginx não consegue ler o arquivo ou atravessar a pasta |
access forbidden by rule | Uma diretiva deny está bloqueando aquele IP ou caminho |
| O log não registra nada | O bloqueio veio de fora do Nginx: SELinux, firewall ou CDN |
Causa 1: try_files ausente ou incorreto
A causa mais comum, e a que gera a mensagem directory index ... is forbidden.
Quando o Nginx recebe uma URL que não corresponde a um arquivo existente, ele precisa saber o que fazer. Sem a diretiva try_files, ele tenta listar o conteúdo da pasta — e como a listagem vem desativada por padrão, o resultado é 403.
Para WordPress e a maioria dos CMS:
location / {
try_files $uri $uri/ /index.php?$args;
}
Para sites estáticos:
location / {
try_files $uri $uri/ =404;
}
Confirme também que os arquivos índice estão declarados no bloco server:
index index.php index.html index.htm;
E verifique que o arquivo índice existe de fato na raiz definida pela diretiva root. Parece óbvio, mas depois de uma migração é comum a raiz apontar para uma pasta acima ou abaixo da correta.
Sobre o autoindex: você pode ativar a listagem de diretório com autoindex on;, o que faz o 403 virar uma lista de arquivos. Não faça isso para resolver o erro — expor o conteúdo das pastas é risco de segurança. Use só em diretórios de download onde a listagem é intencional.
Causa 2: permissões e o usuário do Nginx
A mensagem Permission denied no log aponta para cá.
O Nginx roda sob um usuário próprio — www-data no Debian e Ubuntu, nginx no CentOS, AlmaLinux e Rocky. Confirme qual é o seu:
grep "^user" /etc/nginx/nginx.conf
Esse usuário precisa de leitura nos arquivos e execução nas pastas — inclusive em todas as pastas do caminho até chegar ao site. É aqui que muita gente erra: as permissões da pasta do site estão certas, mas uma pasta acima na hierarquia bloqueia a passagem.
# Testar se o usuário do Nginx consegue chegar ao arquivo
sudo -u www-data stat /var/www/seusite/index.php
# Corrigir permissões em massa
sudo find /var/www/seusite -type d -exec chmod 755 {} \;
sudo find /var/www/seusite -type f -exec chmod 644 {} \;
# Ajustar o dono
sudo chown -R www-data:www-data /var/www/seusite
O comando sudo -u www-data stat é o teste definitivo: se ele falhar com “Permission denied”, o problema é permissão em algum ponto do caminho — e a mensagem indica onde.
Nunca use 777 para resolver. Além do risco de segurança, o próprio PHP-FPM costuma recusar executar arquivos com permissão de escrita para todos.
Causa 3: SELinux — a causa que não aparece no log do Nginx
Esta é a que mais consome tempo, porque o log do Nginx não registra nada de anormal. Você vê 403 no navegador, as permissões estão corretas, o try_files está certo — e o log fica em silêncio.
O SELinux vem ativo por padrão em CentOS, AlmaLinux, Rocky Linux e Fedora, e bloqueia o Nginx de acessar diretórios fora dos caminhos que ele considera legítimos. Se você colocou o site em /home/usuario/site ou em qualquer lugar fora de /usr/share/nginx/html, é bem provável que seja isso.
Como confirmar:
# O SELinux está ativo?
getenforce
# Procurar bloqueios recentes no log de auditoria
sudo grep nginx /var/log/audit/audit.log | tail -20
Se getenforce retornar Enforcing e houver entradas com denied no audit.log, é o SELinux.
A solução correta é ajustar o contexto do diretório, não desativar o SELinux:
# Marcar o diretório como conteúdo web
sudo semanage fcontext -a -t httpd_sys_content_t "/caminho/do/site(/.*)?"
sudo restorecon -Rv /caminho/do/site
# Se o site precisa gravar arquivos (uploads do WordPress)
sudo semanage fcontext -a -t httpd_sys_rw_content_t "/caminho/do/site/wp-content/uploads(/.*)?"
sudo restorecon -Rv /caminho/do/site/wp-content/uploads
Você vai encontrar tutoriais mandando rodar setenforce 0 para desativar o SELinux. Isso resolve na hora e deixa o servidor menos protegido — é troca ruim. Ajuste o contexto.
Causa 4: diretivas deny na configuração
Procure por regras de bloqueio nos seus arquivos de configuração:
sudo grep -rn "deny\|allow" /etc/nginx/
Padrões que costumam causar bloqueio indevido:
deny all;dentro de umlocationmais abrangente que o pretendido- Blocos que negam acesso a arquivos ocultos e acabam pegando o
.well-known— o que quebra a validação do Let’s Encrypt - Regras de
allowpor IP herdadas de uma configuração antiga, que bloqueiam todo o resto - Bloqueios por
$http_user_agentque pegam navegadores legítimos junto com os bots
Se encontrar um bloco de arquivos ocultos, a versão segura preserva o .well-known:
location ~ /\.(?!well-known) {
deny all;
}
Causa 5: o erro só em arquivos estáticos ou só no PHP
O comportamento diferente entre os dois tipos de arquivo é uma pista valiosa:
| Sintoma | Onde olhar |
|---|---|
| 403 em imagens, CSS e JS, mas o HTML carrega | Permissões dos arquivos estáticos, ou um location específico para eles com regra restritiva |
| 403 só nas páginas PHP | O bloco location ~ \.php$ e as permissões do socket do PHP-FPM |
| 403 só numa pasta específica | Falta de arquivo índice naquela pasta, ou um location dedicado a ela |
| 403 no site inteiro, de repente | Alteração recente na configuração, ou permissões alteradas por uma migração |
Nginx como proxy do Apache
Uma configuração comum em painéis de hospedagem é o Nginx na frente e o Apache atrás. Nesse arranjo, o 403 pode vir de qualquer um dos dois — e aí o .htaccess volta a importar, porque quem serve o conteúdo é o Apache.
Como distinguir: se a página de erro traz a assinatura do Apache, o bloqueio é dele; se traz a do Nginx, é do Nginx. E os logs são separados — vale checar os dois.
Depois de qualquer alteração
# Valida a sintaxe antes de aplicar
sudo nginx -t
# Só depois, recarregue
sudo systemctl reload nginx
O nginx -t aponta o arquivo e a linha de qualquer erro de sintaxe sem derrubar o servidor. Rode sempre antes do reload — recarregar com configuração inválida pode deixar o site fora do ar.
Configurar Nginx exige controle total do servidor. Os VPS da Homehost dão acesso root, servidor no Brasil, backup automático e suporte técnico em português — para quando o log não estiver dizendo o óbvio.
Ver servidores VPSPerguntas frequentes
O que causa o erro 403 Forbidden no Nginx?
As três causas dominantes são a diretiva try_files ausente ou incorreta, permissões que impedem o usuário do Nginx de ler os arquivos, e o SELinux bloqueando o acesso ao diretório em servidores CentOS, AlmaLinux ou Rocky. Regras deny na configuração e bloqueios de CDN completam a lista.
O Nginx usa arquivo .htaccess?
Não. O Nginx não lê .htaccess — toda a configuração fica centralizada em /etc/nginx/. Se houver um .htaccess na pasta do seu site, ele é ignorado. Por isso, renomear ou apagar esse arquivo não resolve um 403 no Nginx, ao contrário do que muitos tutoriais genéricos sugerem.
O que significa “directory index is forbidden” no log do Nginx?
Significa que o Nginx tentou listar o conteúdo de uma pasta porque não encontrou arquivo índice, e a listagem está desativada por padrão. A solução é configurar corretamente a diretiva try_files e garantir que exista um index.php ou index.html na pasta.
O número da versão na página de erro do Nginx importa?
Não. A página de erro mostra algo como nginx/1.18.0 (Ubuntu) apenas para identificar o servidor. Esse número não tem relação com a causa do bloqueio, e a solução é a mesma em todas as versões.
Como o SELinux causa erro 403 no Nginx?
O SELinux bloqueia o Nginx de acessar diretórios cujo contexto de segurança não permite conteúdo web. O sintoma característico é 403 no navegador com permissões corretas e nada registrado no log do Nginx — o bloqueio aparece em /var/log/audit/audit.log. A solução é ajustar o contexto com semanage e restorecon, não desativar o SELinux.
Qual usuário o Nginx utiliza para ler os arquivos?
Depende da distribuição: www-data no Debian e Ubuntu, nginx no CentOS, AlmaLinux e Rocky. Confirme com grep "^user" /etc/nginx/nginx.conf. Esse usuário precisa de leitura nos arquivos e execução em todas as pastas do caminho até o site.
Ativar o autoindex resolve o erro 403?
Faz o erro sumir, mas não é solução: em vez do 403, o visitante passa a ver a lista de todos os arquivos da pasta, o que é risco de segurança. O correto é configurar o try_files e garantir o arquivo índice. O autoindex só deve ser usado em pastas onde a listagem é intencional.
Recebo 403 só nas imagens e arquivos CSS. Por quê?
Normalmente são permissões dos arquivos estáticos, ou um bloco location específico para eles com alguma regra restritiva. Verifique se esses arquivos têm permissão 644 e se o bloco que trata de extensões estáticas na sua configuração não contém deny.
Veja também
Para o erro 403 em geral — incluindo Apache, WordPress e as soluções do lado do visitante —, veja nosso guia sobre o erro 403 Forbidden. Outros erros comuns no Nginx: 500 Internal Server Error, 502 Bad Gateway e 504 Gateway Timeout.
Conclusão
O 403 no Nginx parece misterioso porque a maioria dos tutoriais fala de .htaccess — arquivo que o Nginx nem lê. Na prática são três suspeitos: o try_files, as permissões do usuário do servidor, e o SELinux nas distribuições baseadas em Red Hat. E o log resolve quase tudo: deixe o tail -f rodando, recarregue a página, e a linha que aparecer aponta a causa. Só não esqueça de rodar nginx -t antes de recarregar a configuração — validar a sintaxe antes de aplicar evita transformar um 403 num site inteiro fora do ar.