OK, descobri uma maneira de fazer isso.
O que fiz foi usar mod_rewrite , que é um módulo do Apache (enviado por padrão com todas as instalações) para reescrever os URLs solicitados na hora. Aqui está o código:
# Custom error pages
ErrorDocument 404 /hidden/404/index.html
ErrorDocument 403 /hidden/403/index.html
# Pretend that /hidden doesn't exist
# (unless this is an internal redirect,
# such as rendering a 404 or 403)
RewriteCond %{ENV:REDIRECT_STATUS} =""
RewriteRule ^/hidden/.* - [L,R=404]
Então, vamos percorrer essas linhas. As diretivas ErrorDocument não foram alteradas - elas apenas dizem qual página deve ser exibida quando um determinado erro ocorre.
A carne real está nas diretivas RewriteCond e RewriteRule . O que a diretiva RewriteRule diz é a seguinte: se uma URL for solicitada cujo caminho coincida com o regex ^/hidden/.* (essencialmente, qualquer caminho que esteja em / hidden ou sua subárvore), deixe a URL inalterada (é isso que o - significa), mas não interpreta mais RewriteRule diretivas (é o que significa L ) e retorna um código de erro 404 (é o que significa R=404 ).
Isso, combinado com a diretiva ErrorDocument 404 /hidden/404/index.html , parece funcionar. O problema, no entanto, é que quando o Apache vai servir /hidden/404/index.html , ele executa essas regras novamente. Isso significa que a diretiva RewriteRule é executada, o que, por sua vez, diz para renderizar o 404, e a coisa toda apenas loops infinitos.
Assim, usamos a diretiva RewriteCond para garantir que isso não aconteça. As diretivas RewriteCond são condicionais - a diretiva RewriteRule abaixo só será executada se a condição for satisfeita. A condição, neste caso, é "a variável de ambiente REDIRECT_STATUS é igual à cadeia vazia". Agora, REDIRECT_STATUS é uma variável de ambiente definida por ErrorDocument e contém o código de status que está sendo redirecionado (portanto, se a diretiva ErrorDocument 404 ... tiver sido acionada, ela será configurada para o valor "404") . No entanto, se nenhuma diretiva ErrorDocument tivesse sido executada, então REDIRECT_STATUS não teria sido definido e, portanto, pedir seu valor simplesmente retornaria a string vazia. Assim, RewriteCond %{ENV:REDIRECT_STATUS} ="" essencialmente diz: "não se incomode em avaliar a diretiva RewriteRule abaixo, a menos que não estejamos no meio de uma página de erro".
note: Se você consultou a documentação de RewriteRule , você deve ter notado que o argumento R deveria executar um redirecionamento explícito. O motivo pelo qual funciona bem, nesse caso, é que há um recurso não documentado do argumento R , que apenas redireciona os códigos de 300 classes. Assim, R=404 não faz um redirecionamento explícito, apenas um interno.