403 Forbidden no Nginx: causas e como resolver

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.

Resposta rápida

Comece pelo log — ele diz a causa em uma linha:

sudo tail -30 /var/log/nginx/error.log

directory index of … is forbiddenFalta try_files ou arquivo índice
Permission deniedPermissões de arquivo ou pasta
access forbidden by ruleUma diretiva deny na configuração
Nada no log do NginxProvavelmente SELinux — veja o log de auditoria

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 .htaccess nã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 .htaccess estão nos blocos server e location da configuração.
  • Qualquer alteração exige recarregar o Nginx para valer, ao contrário do Apache, que lê o .htaccess a cada requisição.

E há um detalhe que confunde muita gente na página de erro:

💡 O número embaixo da mensagem é a versão, não a causa

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 um location mais 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 allow por IP herdadas de uma configuração antiga, que bloqueiam todo o resto
  • Bloqueios por $http_user_agent que 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.

Acesso root, logs completos e suporte que fala a sua língua

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 VPS

Perguntas 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.

Este artigo foi útil?

Obrigado pela resposta!
Picture of Gustavo Gallas

Gustavo Gallas

Analista de sistemas, formado pela PUC-Rio. Programador, gestor de redes e diretor da empresa Homehost. Pai do Bóris, seu pet de estimação. Gosta de rock'n'roll, cerveja artesanal e de escrever sobre assuntos técnicos.

Contato: gustavo.blog@homehost.com.br

Ganhe 30% OFF

Indique seu nome e e-mail,e ganhe um cupom de desconto de 30% para sempre na Homehost!