Acontece que isso foi devido a uma reescrita interna do mod_rewrite, devido a rodar isso no contexto de um aplicativo Symfony2, que reescreve todos os pedidos para o seu controlador app.php. A mesma coisa aconteceria em um CMS como o wordpress que reescreve tudo para index.php, ou em outros esquemas de purificação de url que reescrevem urls internamente. (Observe que isso não é um problema com redirecionamentos de navegador, pois eles resultam em uma nova solicitação inteira, apenas com reescritas internas.) Basicamente após a última reescrita ser concluída, o htaccess e a lógica de configuração do apache são executados novamente e sua execução é executada. que suas variáveis de ambiente final e tal estão definidas. Então, se uma variável foi definida antes de uma reescrita, ela não estará disponível depois (como quando estou tentando definir o cabeçalho na pergunta aqui).
Mesmo que essas duas linhas na questão sejam adjacentes ...
SecAction "pass,setenv:TESTPAGE=1,nolog,id:10001001"
Header always set X-Debug "IsTest" env=TESTPAGE
A primeira linha está em execução durante a fase do corpo da solicitação (modsec Fase 2) conforme os padrões, uma vez que não especifiquei uma fase. A reescrita para app.php (devido a uma regra de .htaccess não mostrada) ocorre depois. E assim, quando o cabeçalho é definido, a variável não está mais presente.
No entanto, todas as variáveis pré-reescritas ainda estão disponíveis com um prefixo 'redirect_'. Então, para corrigir isso, eu preciso escrever assim:
SecAction "pass,setenv:TESTPAGE=1,nolog,id:10001001"
Header always set X-Debug "IsTest" env=REDIRECT_TESTPAGE
Ou, se não tiver certeza de que a solicitação será redirecionada, posso usar as duas versões:
SecAction "pass,setenv:TESTPAGE=1,nolog,id:10001001"
Header always set X-Debug "IsTest" env=TESTPAGE
Header always set X-Debug "IsTest" env=REDIRECT_TESTPAGE
(Pode haver uma maneira de combiná-los; não tenho certeza se você pode usar a lógica OR em instruções 'env' do apache; por favor, comente se sim!)
Editar: A respeito de "negar" não está mais funcionando, isso também deveu-se aos redirecionamentos. O Mod Security estava lançando um erro 500 por padrão, mas a página de erro padrão 500, 500.shtml, não existia. Portanto, a estrutura estava interceptando essa solicitação e enviando-a de volta para app.php, que então consultou a URL da solicitação e começou a carregar a página solicitada originalmente, apesar do erro. Se o ErrorDocument configurado existir, a negação funcionará corretamente e esse documento será exibido.