Traduction:

Héritage de directive dans Nginx et cas particuliers : proxy_pass, uwsgi_pass…

  • Publié le : mar. 01 avril 2025
  • Autaire : Irrlicht

On bricole avec un'e pote pour faire une directive permettant d'éviter le hotlink sur une infra.

Je compare deux morceaux de configuration afin d'illustrer ce qui a été mis en place et pourquoi en comparant deux blocs.

# bloc 1
location /assets/ {
    expires 1d;
    add_header Pragma public;
    add_header Cache-Control "public, no-transform";
    alias /var/www/other-website/assets/;

    # sous-bloc 1
    location ~* \.(?:jpg|gif|png|ico|svg|mp4|ogg|webm|js|css)$ {
        valid_referers none blocked example.com;
        if ($invalid_referer) {
            return   403;
        }
    }
}

Dans ce premier bloc, on filtre tous les liens sur des ressources qui pourrait être hotlink, on précise depuis quels sites (referers) elles peuvent être demandées, et on renvoie une 403 si ça vient d'ailleurs. Les différentes directives du parent (bloc 1) sont correctement hérité du sous-bloc 1 (alias, add_header, expires).

Une partie des ressources est servie via un proxy et donne lieu à un second bloc de configuration :

# bloc 2
location / {
    proxy_pass http://example;

    # sous-bloc 2
    location ~* \.(?:jpg|gif|png|ico|svg|mp4|ogg|webm|js|css)$ {
        valid_referers none blocked example.com;
        if ($invalid_referer) {
            return 403;
        }
        # Répétition de la directive proxy_pass, sans ça, c'est 404
        proxy_pass http://example;
}

Ici c'est la même logique que le bloc 1, mais on passe par un proxy et il est nécessaire de répéter la directive proxy_pass dans le sous-bloc 2, alors que les autres directives sont correctement héritées. Le symptôme en cas d'absence de directive proxy_pass dans le sous-bloc 2 est un renvoi d'une 404 pour les ressources servies en cas de matching avec la regex et que le referer est valide.

Et c'est ça qui nous interrogeait : qu'est-ce qui justifie la nécessité de répéter la directive proxy_pass dans le sous-bloc 2 alors que c'est inutile pour les autres directives et que le bloc 1, très similaire, n'a aucun soucis ?

On a fini par trouver la réponse :

La directive proxy_pass n'est pas héritée par les sous-bloc. C'est parce qu'une telle directive est considérée comme une directive de "gestion de requêtes" ("request handler", voir ci-après) et que celles-ci sont par principe spécifiées pour tous les blocs location. Il n'y a donc pas d'héritage. Il en est de même pour toutes les autres directives de ce type : fastcgi_pass, uwsgi_pass… Je n'ai pas trouvé de liste précise qui me permette de savoir quelles sont les directives concernées. C'est Maxim Dounin qui nous l'indique dans une réponse sur la mailing list Nginx :

"The idea is that a request handler (proxy_pass in your case) is always explicitly set for a location. Hence handlers are not inherited."

Ce que je retiens de nos fouilles d'Internet sur ce sujet :

  • les "regex matching" sont priorisées sur les "prefix matching" (terminologie de la documentation)
  • Un matching de regexp interrompt le processing des requêtes après le premier match (voir la documentation)
  • Les directives de gestion de requête ("request handler") ne sont pas hérité (proxy_pass, uswgi_path, fastcgi_pass…)

Ça m'a tant retourné le crâne que je me suis dit "et si je passais à HAProxy ?" (non).