nginxで特定パスだけクライアント認証を除外しつつtry_filesで内部リダイレクトしたい。

特殊なケースすぎてもう使うことはない設定だと思うけど、全然思い通りにいかなくて悩んでしまったのでメモ。
検証に使ったnginxのバージョンは1.18.0です。

達成したいこと

条件

  • / 全体 および /ignore 内では、php-fpmによりphpが実行できるようにしたい。対象の拡張子は.phpとする。
  • / 全体 および /ignore は設定で try_files $uri $uri/ /index.php?$args; による内部リダイレクトをしているので、https://example.com/index.php は状況に応じてクライアント認証を適用・除外する必要がある。

ダメだった方法

server {
    listen 443 ssl http2;
    ### ...いろいろ省略...

    ### クライアント認証設定
    ssl_client_certificate /etc/nginx/ssl/xxx.pem;
    ssl_verify_client optional;

    location / {
        if ($ssl_client_verify != SUCCESS) {
            return 403;
        }
        try_files $uri $uri/ /index.php?$args;
    }

    location /ignore {
        try_files $uri $uri/ /index.php?$args;
    }

    location ~ \.php$ {
        if ($ssl_client_verify != SUCCESS) {
            return 403;
        }
        fastcgi_pass unix:/run/php-fpm/www.sock;
        ### いろいろ省略
    }
}

locationディレクティブでのifの使用は推奨されていませんが、ifを使わずに特定ディレクトリを除外する方法はおそらく存在しないため、ifを使いました。
If is Evil… when used in location context | NGINX

ぱっと見は動いてるように見えたのですが https://example.com/ignore/example.php にアクセスするとクライアント認証を要求されます。location ~ \.php$が先に評価されるからですね。

今回採用した方法

server {
    listen 443 ssl http2;
    ### ...いろいろ省略...

    ### クライアント認証設定
    ssl_client_certificate /etc/nginx/ssl/xxx.pem;
    ssl_verify_client optional;

    location / {
        if ($ssl_client_verify != SUCCESS) {
            return 403;
        }
        set $verified T;
        try_files $uri $uri/ /index.php?$args;
    }

    location /ignore {
        set $verified T;
        try_files $uri $uri/ /index.php?$args;
    }

    location ~ \.php$ {
        set $check NG;
        if ($ssl_client_verify = SUCCESS) {
            set $check OK;
        }
        if ($verified = T) {
            set $check OK;
        }
        if ($check != OK) {
            return 403;
        }

        fastcgi_pass unix:/run/php-fpm/www.sock;
        ### いろいろ省略
    }
}

認証する必要がない・認証済みの場合を表すフラグを設定して、php実行時にそのフラグを評価するようにしました。ifを使わずに実現する方法がわかったらそちらを使いたいですね…。