Réécritures (rewrites)
Les réécritures permettent de mapper un chemin de requête entrant vers un chemin de destination différent.
Les réécritures agissent comme un proxy d'URL et masquent le chemin de destination, donnant l'impression que l'utilisateur n'a pas changé d'emplacement sur le site. En revanche, les redirections redirigeront vers une nouvelle page et montreront les changements d'URL.
Pour utiliser les réécritures, vous pouvez utiliser la clé rewrites
dans next.config.js
:
module.exports = {
async rewrites() {
return [
{
source: '/about',
destination: '/',
},
]
},
}
Les réécritures sont appliquées au routage côté client. Un <Link href="/about">
aura la réécriture appliquée dans l'exemple ci-dessus.
rewrites
est une fonction asynchrone qui doit retourner soit un tableau, soit un objet de tableaux (voir ci-dessous) contenant des objets avec les propriétés source
et destination
:
source
:String
- le motif de chemin de requête entrant.destination
:String
- le chemin vers lequel vous souhaitez router.basePath
:false
ouundefined
- si false, le basePath ne sera pas inclus lors de la correspondance, peut être utilisé uniquement pour les réécritures externes.locale
:false
ouundefined
- indique si la locale ne doit pas être incluse lors de la correspondance.has
est un tableau d'objets has avec les propriétéstype
,key
etvalue
.missing
est un tableau d'objets missing avec les propriétéstype
,key
etvalue
.
Lorsque la fonction rewrites
retourne un tableau, les réécritures sont appliquées après la vérification du système de fichiers (pages et fichiers /public
) et avant les routes dynamiques. Lorsque la fonction rewrites
retourne un objet de tableaux avec une forme spécifique, ce comportement peut être modifié et contrôlé plus finement, à partir de v10.1
de Next.js :
module.exports = {
async rewrites() {
return {
beforeFiles: [
// Ces réécritures sont vérifiées après les en-têtes/redirections
// et avant tous les fichiers, y compris les fichiers _next/public, ce qui
// permet de remplacer les fichiers de page
{
source: '/some-page',
destination: '/somewhere-else',
has: [{ type: 'query', key: 'overrideMe' }],
},
],
afterFiles: [
// Ces réécritures sont vérifiées après les fichiers pages/public
// mais avant les routes dynamiques
{
source: '/non-existent',
destination: '/somewhere-else',
},
],
fallback: [
// Ces réécritures sont vérifiées après les fichiers pages/public
// et les routes dynamiques
{
source: '/:path*',
destination: `https://my-old-site.com/:path*`,
},
],
}
},
}
Bon à savoir : les réécritures dans
beforeFiles
ne vérifient pas immédiatement le système de fichiers/routes dynamiques après avoir trouvé une correspondance, elles continuent jusqu'à ce que toutes lesbeforeFiles
aient été vérifiées.
L'ordre dans lequel Next.js vérifie les routes est :
- Les en-têtes sont vérifiés/appliqués
- Les redirections sont vérifiées/appliquées
- Les réécritures
beforeFiles
sont vérifiées/appliquées - Les fichiers statiques du répertoire public, les fichiers
_next/static
et les pages non dynamiques sont vérifiés/servis - Les réécritures
afterFiles
sont vérifiées/appliquées. Si l'une de ces réécritures correspond, nous vérifions les routes dynamiques/fichiers statiques après chaque correspondance - Les réécritures
fallback
sont vérifiées/appliquées. Elles sont appliquées avant le rendu de la page 404 et après la vérification des routes dynamiques/tous les assets statiques. Si vous utilisez fallback: true/'blocking' dansgetStaticPaths
, les réécrituresfallback
définies dans votrenext.config.js
ne seront pas exécutées.
Paramètres de réécriture
Lorsque vous utilisez des paramètres dans une réécriture, les paramètres seront passés dans la requête par défaut lorsqu'aucun des paramètres n'est utilisé dans la destination
.
module.exports = {
async rewrites() {
return [
{
source: '/old-about/:path*',
destination: '/about', // Le paramètre :path n'est pas utilisé ici, donc il sera automatiquement passé dans la requête
},
]
},
}
Si un paramètre est utilisé dans la destination, aucun des paramètres ne sera automatiquement passé dans la requête.
module.exports = {
async rewrites() {
return [
{
source: '/docs/:path*',
destination: '/:path*', // Le paramètre :path est utilisé ici, donc il ne sera pas automatiquement passé dans la requête
},
]
},
}
Vous pouvez toujours passer les paramètres manuellement dans la requête si l'un est déjà utilisé dans la destination en spécifiant la requête dans la destination
.
module.exports = {
async rewrites() {
return [
{
source: '/:first/:second',
destination: '/:first?second=:second',
// Comme le paramètre :first est utilisé dans la destination, le paramètre :second
// ne sera pas automatiquement ajouté dans la requête, bien que nous puissions l'ajouter
// manuellement comme montré ci-dessus
},
]
},
}
Bon à savoir : Les pages statiques de l'optimisation statique automatique ou les paramètres de prérendu des réécritures seront analysés côté client après l'hydratation et fournis dans la requête.
Correspondance de chemin
Les correspondances de chemin sont autorisées, par exemple /blog/:slug
correspondra à /blog/hello-world
(pas de chemins imbriqués) :
module.exports = {
async rewrites() {
return [
{
source: '/blog/:slug',
destination: '/news/:slug', // Les paramètres correspondants peuvent être utilisés dans la destination
},
]
},
}
Correspondance de chemin avec joker
Pour correspondre à un chemin avec joker, vous pouvez utiliser *
après un paramètre, par exemple /blog/:slug*
correspondra à /blog/a/b/c/d/hello-world
:
module.exports = {
async rewrites() {
return [
{
source: '/blog/:slug*',
destination: '/news/:slug*', // Les paramètres correspondants peuvent être utilisés dans la destination
},
]
},
}
Correspondance de chemin avec regex
Pour correspondre à un chemin regex, vous pouvez encapsuler la regex entre parenthèses après un paramètre, par exemple /blog/:slug(\\d{1,})
correspondra à /blog/123
mais pas à /blog/abc
:
module.exports = {
async rewrites() {
return [
{
source: '/old-blog/:post(\\d{1,})',
destination: '/blog/:post', // Les paramètres correspondants peuvent être utilisés dans la destination
},
]
},
}
Les caractères suivants (
, )
, {
, }
, [
, ]
, |
, \
, ^
, .
, :
, *
, +
, -
, ?
, $
sont utilisés pour la correspondance de chemin regex, donc lorsqu'ils sont utilisés dans la source
comme valeurs non spéciales, ils doivent être échappés en ajoutant \\
avant eux :
module.exports = {
async rewrites() {
return [
{
// cela correspondra à `/english(default)/something` demandé
source: '/english\\(default\\)/:slug',
destination: '/en-us/:slug',
},
]
},
}
Correspondance d'en-tête, de cookie et de requête
Pour ne correspondre à une réécriture que lorsque les valeurs d'en-tête, de cookie ou de requête correspondent également, le champ has
peut être utilisé, ou le champ missing
pour ne pas correspondre. À la fois la source
et tous les éléments has
doivent correspondre, et tous les éléments missing
ne doivent pas correspondre pour que la réécriture soit appliquée.
Les éléments has
et missing
peuvent avoir les champs suivants :
type
:String
- doit être soitheader
,cookie
,host
ouquery
.key
:String
- la clé du type sélectionné à comparer.value
:String
ouundefined
- la valeur à vérifier. Si undefined, n'importe quelle valeur correspondra. Une chaîne de type regex peut être utilisée pour capturer une partie spécifique de la valeur, par exemple si la valeurfirst-(?<paramName>.*)
est utilisée pourfirst-second
, alorssecond
pourra être utilisé dans la destination avec:paramName
.
module.exports = {
async rewrites() {
return [
// si l'en-tête `x-rewrite-me` est présent,
// cette réécriture sera appliquée
{
source: '/:path*',
has: [
{
type: 'header',
key: 'x-rewrite-me',
},
],
destination: '/another-page',
},
// si l'en-tête `x-rewrite-me` n'est pas présent,
// cette réécriture sera appliquée
{
source: '/:path*',
missing: [
{
type: 'header',
key: 'x-rewrite-me',
},
],
destination: '/another-page',
},
// si la source, la requête et le cookie correspondent,
// cette réécriture sera appliquée
{
source: '/specific/:path*',
has: [
{
type: 'query',
key: 'page',
// la valeur page ne sera pas disponible dans la
// destination car la valeur est fournie et n'utilise pas
// un groupe de capture nommé, par exemple (?<page>home)
value: 'home',
},
{
type: 'cookie',
key: 'authorized',
value: 'true',
},
],
destination: '/:path*/home',
},
// si l'en-tête `x-authorized` est présent et
// contient une valeur correspondante, cette réécriture sera appliquée
{
source: '/:path*',
has: [
{
type: 'header',
key: 'x-authorized',
value: '(?<authorized>yes|true)',
},
],
destination: '/home?authorized=:authorized',
},
// si l'hôte est `example.com`,
// cette réécriture sera appliquée
{
source: '/:path*',
has: [
{
type: 'host',
value: 'example.com',
},
],
destination: '/another-page',
},
]
},
}
Réécriture vers une URL externe
Les réécritures permettent de réécrire vers une URL externe. Cela est particulièrement utile pour l'adoption progressive de Next.js. Voici un exemple de réécriture pour rediriger la route /blog
de votre application principale vers un site externe.
module.exports = {
async rewrites() {
return [
{
source: '/blog',
destination: 'https://example.com/blog',
},
{
source: '/blog/:slug',
destination: 'https://example.com/blog/:slug', // Les paramètres correspondants peuvent être utilisés dans la destination
},
]
},
}
Si vous utilisez trailingSlash: true
, vous devez également insérer une barre oblique finale dans le paramètre source
. Si le serveur de destination attend également une barre oblique finale, elle doit être incluse dans le paramètre destination
.
module.exports = {
trailingSlash: true,
async rewrites() {
return [
{
source: '/blog/',
destination: 'https://example.com/blog/',
},
{
source: '/blog/:path*/',
destination: 'https://example.com/blog/:path*/',
},
]
},
}
Adoption progressive de Next.js
Vous pouvez également faire en sorte que Next.js se rabatte sur un proxy vers un site web existant après avoir vérifié toutes les routes Next.js.
De cette façon, vous n'avez pas besoin de modifier la configuration des réécritures lors de la migration de plus de pages vers Next.js.
module.exports = {
async rewrites() {
return {
fallback: [
{
source: '/:path*',
destination: `https://custom-routes-proxying-endpoint.vercel.app/:path*`,
},
],
}
},
}
Réécritures avec support de basePath
Lorsque vous utilisez le support de basePath
avec les réécritures, chaque source
et destination
est automatiquement préfixé avec le basePath
sauf si vous ajoutez basePath: false
à la réécriture :
module.exports = {
basePath: '/docs',
async rewrites() {
return [
{
source: '/with-basePath', // devient automatiquement /docs/with-basePath
destination: '/another', // devient automatiquement /docs/another
},
{
// n'ajoute pas /docs à /without-basePath car basePath: false est défini
// Note : cela ne peut pas être utilisé pour les réécritures internes, par exemple `destination: '/another'`
source: '/without-basePath',
destination: 'https://example.com',
basePath: false,
},
]
},
}
Réécritures avec support i18n
Lorsque vous utilisez le support i18n
avec les réécritures, chaque source
et destination
est automatiquement préfixé pour gérer les locales
configurées, sauf si vous ajoutez locale: false
à la réécriture. Si locale: false
est utilisé, vous devez préfixer la source
et la destination
avec une locale pour qu'elle corresponde correctement.
module.exports = {
i18n: {
locales: ['en', 'fr', 'de'],
defaultLocale: 'en',
},
async rewrites() {
return [
{
source: '/with-locale', // gère automatiquement toutes les locales
destination: '/another', // passe automatiquement la locale
},
{
// ne gère pas automatiquement les locales car locale: false est défini
source: '/nl/with-locale-manual',
destination: '/nl/another',
locale: false,
},
{
// cela correspond à '/' car `en` est la defaultLocale
source: '/en',
destination: '/en/another',
locale: false,
},
{
// il est possible de correspondre à toutes les locales même lorsque locale: false est défini
source: '/:locale/api-alias/:path*',
destination: '/api/:path*',
locale: false,
},
{
// cela est converti en /(en|fr|de)/(.*) donc ne correspondra pas aux routes
// de premier niveau `/` ou `/fr` comme /:path* le ferait
source: '/(.*)',
destination: '/another',
},
]
},
}
Historique des versions
Version | Modifications |
---|---|
v13.3.0 | missing ajouté. |
v10.2.0 | has ajouté. |
v9.5.0 | En-têtes ajoutés. |