URLs conviviales, réécriture d'URL et reverse proxy
Ce document explique comment intégrer Mindtraining Platform avec des URLs conviviales et comment configurer la réécriture d'URL ou un reverse proxy pour que la navigation directe, les favoris et le rafraîchissement fonctionnent correctement.
Table des matières
Le problème
Mindtraining Platform utilise un routage côté client : le navigateur charge un unique index.html (ou équivalent) et JavaScript met à jour la vue en fonction du chemin de l'URL. Quand un utilisateur :
Accède directement à
https://yoursite.com/games/crossword/archiveMet en favori un deep link
Rafraîchit la page sur une route autre que la racine
…le navigateur envoie une requête au serveur pour ce chemin exact. Sans configuration, le serveur cherche un fichier à /games/crossword/archive et renvoie 404, car ce chemin n'existe que dans le routeur SPA, pas sur disque.
La solution
Toutes les requêtes visant des routes SPA doivent servir le même fichier d'entrée (index.html ou script.js). La SPA lit ensuite l'URL et affiche la bonne vue. Cela se fait via :
URL rewrite — mapper en interne toutes les routes SPA vers le fichier d'entrée
Reverse proxy — transférer les requêtes vers un backend qui sert la SPA
Fallback / catch-all — traiter toute route non statique comme une route SPA
Apache
Avec .htaccess (mod_rewrite)
Placez ceci dans la racine du document ou dans le sous-répertoire de la SPA (par ex. /games/.htaccess) :
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /games/
# Ne pas réécrire les fichiers ou dossiers existants
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
# Réécrire tout le reste vers index.html
RewriteRule ^ index.html [L]
</IfModule>Notes :
Remplacez
/games/par votre base path SPA. Utilisez/si la SPA est à la racine.Vérifiez que
mod_rewriteest activé :a2enmod rewrite(Debian/Ubuntu).AllowOverride Alldoit être défini pour que.htaccesssoit pris en compte.
Avec VirtualHost (sans .htaccess)
<VirtualHost *:80>
ServerName yoursite.com
DocumentRoot /var/www/mindtraining
<Directory /var/www/mindtraining>
Options -Indexes +FollowSymLinks
AllowOverride None
Require all granted
RewriteEngine On
RewriteBase /games/
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^ index.html [L]
</Directory>
</VirtualHost>SPA dans un sous-répertoire (par ex. /games)
Si la SPA vit sous /games et que votre point d'entrée est index.html dans ce dossier :
<Directory /var/www/html/games>
RewriteEngine On
RewriteBase /games/
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^ /games/index.html [L]
</Directory>Nginx
Fallback SPA basique
server {
listen 80;
server_name yoursite.com;
root /var/www/mindtraining;
location / {
try_files $uri $uri/ /index.html;
}
}SPA dans un sous-répertoire (par ex. /games)
server {
listen 80;
server_name yoursite.com;
root /var/www/html;
location /games {
alias /var/www/html/games;
try_files $uri $uri/ /games/index.html;
}
}Avec reverse proxy vers une origine statique/CDN
Si la SPA est servie depuis un CDN ou une autre origine :
server {
listen 80;
server_name yoursite.com;
location / {
proxy_pass <https://cdn.example.com/mindtraining/>;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Fallback SPA : si l'upstream répond 404, servir index
proxy_intercept_errors on;
error_page 404 = /index.html;
}
}Sous-chemin + proxy plus robuste
server {
listen 80;
server_name yoursite.com;
location /games/ {
proxy_pass <https://cdn.example.com/mindtraining/>;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Ne pas réécrire les assets statiques
proxy_intercept_errors on;
proxy_next_upstream error timeout http_404;
error_page 404 = @spa_fallback;
}
location @spa_fallback {
rewrite ^ /games/index.html break;
proxy_pass <https://cdn.example.com/mindtraining/>;
proxy_http_version 1.1;
proxy_set_header Host $host;
}
}Alternatives (sans Apache/Nginx)
Si vous ne pouvez pas utiliser Apache ou Nginx (par ex. serverless, PaaS ou simple serveur Node), utilisez l'une des approches suivantes.
1. Node.js (Express)
const express = require('express')
const path = require('path')
const app = express()
const PORT = process.env.PORT || 3000
// Servir les fichiers statiques (JS, CSS, images)
app.use(express.static(path.join(__dirname, 'dist')))
// Fallback SPA : toutes les autres routes servent index.html
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, 'dist', 'index.html'))
})
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`)
})SPA dans un sous-répertoire (par ex. /games) :
const express = require('express')
const path = require('path')
const app = express()
const BASE = '/games'
app.use(BASE, express.static(path.join(__dirname, 'dist')))
app.get(`${BASE}/*`, (req, res) => {
res.sendFile(path.join(__dirname, 'dist', 'index.html'))
})
app.listen(process.env.PORT || 3000)2. Vercel (vercel.json)
{
"rewrites": [
{ "source": "/(.*)", "destination": "/index.html" }
]
}Pour un sous-chemin :
{
"rewrites": [
{ "source": "/games/:path*", "destination": "/games/index.html" }
]
}3. Netlify (_redirects ou netlify.toml)
_redirects (dans public/ ou à la racine du projet) :
/* /index.html 200netlify.toml :
[[redirects]]
from = "/*"
to = "/index.html"
status = 200Pour le sous-chemin /games :
/games/* /games/index.html 2004. AWS S3 + CloudFront (hébergement statique)
S3 ne prend pas en charge les rewrites. Utilisez CloudFront Functions ou Lambda@Edge :
CloudFront Function (viewer request ou origin request) :
function handler(event) {
var request = event.request
var uri = request.uri
// Ne pas réécrire si cela ressemble à un fichier
if (uri.includes('.') && !uri.endsWith('.html')) {
return request
}
// Fallback SPA
if (!uri.endsWith('/') && !uri.includes('.')) {
request.uri = '/index.html'
} else if (uri.endsWith('/')) {
request.uri = uri + 'index.html'
}
return request
}5. Firebase Hosting (firebase.json)
{
"hosting": {
"public": "dist",
"ignore": ["firebase.json", "**/.*", "**/node_modules/**"],
"rewrites": [
{
"source": "**",
"destination": "/index.html"
}
]
}
}6. GitHub Pages
GitHub Pages ne prend pas en charge les rewrites côté serveur. Options :
Utiliser l'astuce 404.html : créer une page 404 personnalisée qui charge la SPA et redirige côté client. Ce n'est pas idéal pour le SEO ni pour les deep links.
Utiliser un hash router côté client (
#/games/crossword) au lieu d'un routage basé sur le path. Aucune config serveur n'est nécessaire, mais les URLs sont moins propres.Héberger la SPA ailleurs (Vercel, Netlify, etc.) et pointer votre domaine vers cet hébergement.
Spécificités de Mindtraining Platform
Cette plateforme utilise TanStack Router avec un basepath dynamique venant de l'API. Routes typiques :
| Pattern de route | Exemple |
|---|---|
| Accueil | / ou /{basepath}/ |
| Jeu du jour | /{basepath}/crossword/ |
| Archive | /{basepath}/crossword/archive |
| Statistiques | /{basepath}/crossword/statistics |
| Date spécifique | /{basepath}/crossword/2024-03-18 |
Le basepath est configuré par site/domaine. Assurez-vous que vos règles couvrent tout le basepath. Par exemple, si basepath vaut /games :
Apache :
RewriteBase /games/et servirindex.htmlpour toutes les requêtes non fichier sous/gamesNginx :
location /games { try_files $uri $uri/ /games/index.html; }Express : monter les assets statiques + le fallback sous
/games