Laisser un commentaire

Apache & Nginx : servir des fichiers sans extension

Les pretty urls comme on les appelles communément, consistent à faire abstraction de l’extension des fichiers dans les urls. Ainsi, https://buzut.net/a-propos.html se transforme en https://buzut.net/a-propos. C’est plus court, c’est plus clean, c’est plus SEO, bref, c’est plus mieux. Voyons comment faire cela avec les serveurs les plus courants : Apache2 et Nginx.

Vous noterez que certains sites servent toutes leurs urls avec un slash de fin. Il s’agit souvent d’une volonté esthétique. Cela dit, on distingue en général les répertoires, qui se terminent par un slash, des fichiers, qui ne se terminent pas par un slash.

Ainsi, https://buzut.net/test/ fait référence au répertoire nommé “test”, tandis que https://buzut.net/test fait référence à un fichier “test” placé à la racine du site.

Bien que ce blog ne respecte pas cette convention (CMS inside), nous allons tout de même nous conformer à cette dernière dans la suite de cet article.

Le principe

Commençons par un micro cahier des charges. Il y a trois critères à respecter :

Petit détail, lorsqu’on détecte index.html, on ne redirige pas comme un bourrin sur la racine. En effet, de nombreux sites possèdent des index à d’autres niveaux. Pour la version localisée par exemple, il serait mal venu d’être redirigé vers une page dans une autre langue…

Bref, assez discuté, commençons !

Apache2

RewriteEngine on
RewriteBase /

RewriteCond %{REQUEST_URI} ^(.*)index\.html$ [NC]
RewriteRule ^ %1 [R=301,L]

RewriteCond %{THE_REQUEST} ^[A-Z]{3,}\s([^.]+)\.html [NC]
RewriteRule ^ %1 [R=301,L]

RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)/$ $1 [R=301,L]

RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}\.html -f
RewriteRule ^(.*)$ $1.html

Nous avons ici cinq blocs distincts. Expliquons rapidement le rôle de chacun. On commence par activer la redirection d’url – il faut tout de même que le mod_rewrite soit actif – puis on définit ensuite la base pour les chemins relatifs.

Ensuite, pour toute url contenant index.html, on capture ce qui est avant index.html et on le réécrit sans le fichier. Donc http://monsite.fr/en/index.html sera bien redirigé vers http://monsite.fr/en/ et non http://monsite.fr/.

Le bloc suivant capture toutes les url contenant l’extension .html et réécrit l’url sans cette dernière.

Notez bien que dans les deux paragraphes précédents, lorsque je parle de réécriture, il s’agit bien ici de redirections de type 301 (R=301).

L’avant dernier bloc permet d’enlever le slash de fin d’url s’il ne s’agit pas d’un répertoire. Cela planterait Apache à cause d’une bloucle de redirection. Il est très improbable que nous ayons des noms de fichier du type fichier/.html.

Enfin, le dernier bloc fait en sorte qu’une requête qui ne mène théoriquement à rien – chez A Partners, dans https://a-partners.eu/team, contact n’existe pas, c’est bien contact.html qui existe – serve le bon contenu.

Pour se faire, on vérifie que l’url ne fait pas référence à un répertoire et que si on la suffixe par .html, cela correspond à un fichier. Si ces deux conditions sont remplies, alors on sert le fichier en question.

Si vous souhaitez creusez tout cela, je vous laisse entre les mains de la doc Apache.

Nginx

Nous allons faire exactement la même chose avec Nginx. De nombreux exemples trainent sur le web, dont un certain nombre ne fonctionnent pas (ou plus avec les versions récentes de Nginx). En outre, on lit souvent qu’il ne faut pas utiliser les if. C’est vrai dans de nombreux cas, mais la doc de Nginx dans son if is evil, précise bien que l’usage que nous allons en faire est 100% safe.

Il est possible de se passer des if en utilisant plusieurs bloc location, mais il n’est pas possible avec cette technique de différencier index.html des autres fichiers.

Nous allons donc utiliser des if. C’est en plus bien plus concis et plus court. Cela dit, je suis ouvert à toute remarque ou suggestion dans les commentaires.

location / {
  if ($request_uri ~ ^/(.*)index(\.html)?$) {  return 301 /$1;  }
  if ($request_uri ~ ^/(.*)\.html$) {  return 301 /$1;  }
  try_files $uri $uri.html $uri/ =404;
}

Dans notre bloc s’appliquant à la racine, nous testons si l’url contient index.html ou index tout court, si c’est le cas, on capture ce qui précède et on redirige en enlevant juste la partie superflue ; sinon, on teste si l’url contient .html, si oui on redirige en enlevant l’extension.

Enfin, dans tous les autres cas, on utilise try_files pour servir le fichier/répertoire. Dans l’ordre :

Sachez que, de par la comportement des directives location (s’il y a plusieurs matchs, le plus long est pris en compte), des directives générales comme celle-ci pour gérer l’expiration des contenus, empêcheront votre bloc de s’exécuter.

La doc est toujours disponible si vous voulez creuser un peu plus le fonctionnement des blocs location ou des try_files.

Et voilà, que vous utilisez Nginx ou Apache, vous n’avez maintenant plus aucune excuse pour ne pas avoir des url bien proprettes ! N’hésitez pas à utiliser les commentaires si vous avez des remarques et/ou une autre approche.

Commentaires

Rejoignez la discussion !

Vous pouvez utiliser Markdown pour les liens [ancre de lien](url), la mise en *italique* et en **gras**. Enfin pour le code, vous pouvez utiliser la syntaxe `inline` et la syntaxe bloc

```
ceci est un bloc
de code
```