Richtige .htaccess-Konfiguration für Next.js SSG
NextJS exportiert eine statische Site mit der folgenden Struktur:
|-- index.html
|-- article.html
|-- tag.html
|-- article
| |-- somearticle.html
| \-- anotherarticle.html
\-- tag
|-- tag1.html
\-- tag2.html
Ich verwende eine .htaccess-Datei, um die .html-Erweiterungen auszublenden:
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}\.html -f
RewriteRule ^(.*)$ $1.html
Alles funktioniert einwandfrei, AUSSER:
- Wenn ich einem Link dazu folge,
domain/article
wird die Seite article.html angezeigt, in meiner Adressleiste wird jedochdomain/article
<- Gut angezeigt . - Wenn ich aktualisiere, werde ich an folgende Adresse gesendet:
domain/article/
(nachgestellter Schrägstrich beachten), in der der Inhalt des Artikelverzeichnisses <- Bad aufgelistet ist (dasselbe mit Tag) - In ähnlicher Weise
domain/article
führt mich das manuelle Eingeben dazu,domain/article/
anstattarticle.html
ohne die.html
Erweiterung anzuzeigen.
Damit...
- Wie behebe ich das?
- Ist das ein .htaccess-Problem?
- Ein nextjs Konfigurationsproblem?
- (Wäre es für NextJS nicht besser,
article\index.html
eine Datei anstelle einer Datei im Stammverzeichnis zu erstellen ?)
exportTrailingSlash
Ich habe versucht, damit herumzuspielen, exportTrailingSlash
was verwandt zu sein scheint, aber dies verursachte andere Probleme, wie immer einen abschließenden Schrägstrich am Ende aller meiner Links:
Beispiel: Wenn ich domain/article/somearticle
auf "Aktualisieren" gehe, fügt etwas (.httaccess?) Am /
Ende ein hinzu, um mich domain/article/somearticle/
nicht schrecklich, nur nicht sehr sauber und inkonsistent zu machen ...
Bearbeiten: Eigentlich ist es ein bisschen schrecklicher, weil wir manchmal einen abschließenden Schrägstrich bekommen, manchmal nicht auf den nextjs-Links ... muss etwas über meine Verwendung sein,<Link />
aber ich kann das nicht herausfinden.
Unabhängig davon, KEINE der .htaccess
Regeln, die ich versucht habe, den abschließenden Schrägstrich jedes Mal erfolgreich zu entfernen ...
Mehr Details:
In meiner nächsten App habe ich Ordner:
/articles/
[slug].js
index.js
Auf verschiedenen Seiten verwende ich die nextJS Link-Komponente:
import Link from 'next/link';
<Link href="/articles" as="/articles">
<a>Articles</a>
</Link>
Antworten
Wenn Sie ein physisches Verzeichnis anfordern /article
und /article
als solches existieren, hängt Apaches mod_dir (standardmäßig) den abschließenden Schrägstrich an, um die URL zu "reparieren". Dies wird mit einer permanenten 301-Umleitung erreicht - sie wird also vom Browser zwischengespeichert.
Obwohl ein physisches Verzeichnis mit demselben Basisnamen wie eine Datei und die Verwendung von URLs ohne Erweiterung eine Mehrdeutigkeit erzeugt. z.B. Soll /article
auf das Verzeichnis /article/
oder die Datei zugreifen /article.html
. Sie scheinen ohnehin keinen direkten Zugriff auf Verzeichnisse zuzulassen, daher scheint dies diese Unklarheit zu beseitigen.
Um zu verhindern, dass Apache mod_dir den abschließenden Schrägstrich an Verzeichnisse anfügt, müssen wir den deaktivieren DirectorySlash
. Zum Beispiel:
DirectorySlash Off
Wie bereits erwähnt, wurde /article
die Umleitung zu , wenn Sie sie zuvor besucht /article/
haben, vom Browser zwischengespeichert. Sie müssen daher den Browser-Cache leeren, bevor dies wirksam wird.
Da Sie die Dateierweiterung entfernen, müssen Sie auch sicherstellen, dass MultiViews deaktiviert ist. Andernfalls gibt mod_negotiation eine interne Unteranforderung für die zugrunde liegende Datei aus und kann möglicherweise zu Konflikten mit mod_rewrite führen. MultiViews ist standardmäßig deaktiviert, obwohl einige gemeinsam genutzte Hosts es aus irgendeinem Grund aktivieren. Von der Ausgabe, die Sie erhalten, sieht es nicht so aus, als ob MultiViews aktiviert ist, aber besser, um sicherzugehen ...
# Ensure that MutliViews is disabled
Options -MultiViews
Wenn Sie jedoch auf das Verzeichnis selbst zugreifen möchten, müssen Sie den abschließenden Schrägstrich manuell mit einem internen Umschreiben anhängen. Dies scheint hier jedoch keine Voraussetzung zu sein. Sie sollten jedoch sicherstellen, dass Verzeichnislisten deaktiviert sind:
# Disable directory listings
Options -Indexes
Wenn Sie versuchen, auf ein Verzeichnis zuzugreifen (das letztendlich keiner Datei zugeordnet ist - siehe unten) und kein DirectoryIndex
Dokument enthält , wird anstelle einer Verzeichnisliste eine 403 Forbidden-Antwort zurückgegeben.
Beachten Sie, dass der einzige Unterschied zwischen dem Folgen eines Links zu domain/article
, dem Aktualisieren der Seite und dem manuellen Eingeben domain/article
das Zwischenspeichern ist ... entweder durch den Browser oder durch einen zwischengeschalteten Proxy-Cache. (Es sei denn, Sie haben JavaScript, das das Klickereignis auf dem Anker abfängt?!)
Abhängig davon, wie Sie Ihre Site konfiguriert haben, müssen Sie noch Anforderungen von /foo
nach /foo.html
ODER /foo
nach /foo/index.html
(siehe unten) umschreiben . Obwohl es vorzuziehen wäre, dass Sie sich für das eine oder das andere entscheiden, anstatt für beide (wie Sie anscheinend implizieren, könnte dies der Fall sein).
RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME}\.html -f RewriteRule ^(.*)$ $1.html
Es ist unklar, wie dies derzeit für Sie "funktioniert" - es sei denn, Sie sehen eine zwischengespeicherte Antwort? Wenn Sie eine Anforderung anfordern /article
, schlägt die erste Bedingung fehl, da diese als physisches Verzeichnis vorhanden ist und die Regel nicht verarbeitet wird. Selbst wenn MultiViews aktiviert sind, hat mod_dir Priorität und hängt den abschließenden Schrägstrich an.
Die zweite Bedingung, die das Vorhandensein der .html
Datei überprüft, überprüft nicht unbedingt dieselbe Datei, in die neu geschrieben wird. z.B. Wenn Sie anfordern /foo/bar
, wo /foo.html
vorhanden, aber kein physisches Verzeichnis vorhanden ist, prüft /foo
die RewriteCond
Direktive auf das Vorhandensein von /foo.html
- was erfolgreich ist, aber die Anforderung wird intern neu geschrieben /foo/bar.html
(aus dem erfassten RewriteRule
Muster ) -, was zu einer internen Umschreibschleife und einer 500 führt Fehlerantwort wird an den Client zurückgegeben. Siehe meine Antwort auf die folgende ServerFault-Frage , die detaillierter beschreibt, was hier tatsächlich passiert.
Wir können auch eine weitere Optimierung machen , wenn wir , dass jede URL annehmen , das enthält , was wie eine Dateierweiterung aussieht (zB. Ihre statischen Ressourcen .css
, .js
und Bilddateien) ignoriert werden soll, andernfalls werden wir System Checks durchführen bei jeder Anfrage, die relativ teuer ist .
Also, um (intern neu schreiben) abzubilden Anfragen der Form /article
auf /article.html
und /article/somearticle
zu /article/somearticle.html
müßten Sie die obige Regel zu modifizieren , so etwas wie zu lesen:
# Rewrite /foo to /foo.html if it exists
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI}.html -f
RewriteRule !\.\w{2,4}$ %{REQUEST_URI}.html [L]
Es ist nicht erforderlich, einen wörtlichen Punkt im RewriteCond
TestString mit einem Backslash zu versehen - der Punkt hat hier keine besondere Bedeutung. Es ist kein regulärer Ausdruck.
Dann auf Anfragen der Form zu verarbeiten /foo
, die Karte sollten /foo/index.html
Sie so etwas wie folgendes tun:
# Rewrite /foo to /foo/index.html if it exists
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI}/index.html -f
RewriteRule !\.\w{2,4}$ %{REQUEST_URI}/index.html [L]
Normalerweise erlauben Sie mod_dir, das DirectoryIndex
(z. B. index.html
) zu bedienen , aber nachdem Sie den abschließenden Schrägstrich aus dem Verzeichnis weggelassen haben, kann dies problematisch sein.
Zusammenfassung
Wenn wir die oben genannten Punkte zusammenbringen, haben wir:
# Disable directory indexes and MultiViews
Options -Indexes -MultiViews
# Prevent mod_dir appending a slash to directory requests
DirectorySlash Off
# Rewrite /foo to /foo.html if it exists
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI}.html -f
RewriteRule !\.\w{2,4}$ %{REQUEST_URI}.html [L] # Otherwise, rewrite /foo to /foo/index.html if it exists RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI}/index.html -f RewriteRule !\.\w{2,4}$ %{REQUEST_URI}/index.html [L]
Dies kann weiter optimiert werden, abhängig von Ihrer Site-Struktur und davon, ob Sie der .htaccess
Datei weitere Anweisungen hinzufügen . Zum Beispiel:
- Sie können die angeforderte URL oben in der Datei nach Dateierweiterungen durchsuchen, um eine weitere Verarbeitung zu verhindern. Die
RewriteRule
Regex für jede nachfolgende Regel könnte dann "vereinfacht" werden. - Anforderungen, die einen abschließenden Schrägstrich enthalten, können blockiert oder umgeleitet werden (um den abschließenden Schrägstrich zu entfernen).
- Wenn die Anforderung eine
.html
Datei ist, leiten Sie zur URL ohne Erweiterung um. Dies wird etwas komplizierter, wenn Sie mit beiden/foo.html
und/foo/index.html
. Dies ist jedoch nur dann wirklich erforderlich, wenn Sie eine vorhandene URL-Struktur ändern .
Wenn Sie beispielsweise die obigen Nummern 1 und 2 implementieren, können die Anweisungen folgendermaßen geschrieben werden:
# Disable directory indexes and MultiViews
Options -Indexes -MultiViews
# Prevent mod_dir appending a slash to directory requests
DirectorySlash Off
# Prevent any further processing if the URL already ends with a file extension
RewriteRule \.\w{2.4}$ - [L] # Redirect any requests to remove a trailing slash RewriteRule (.*)/$ /$1 [R=301,L] # Rewrite /foo to /foo.html if it exists RewriteCond %{DOCUMENT_ROOT}/$1.html -f
RewriteRule (.*) $1.html [L] # Otherwise, rewrite /foo to /foo/index.html if it exists RewriteCond %{DOCUMENT_ROOT}/$1/index.html -f
RewriteRule (.*) $1/index.html [L]
Testen Sie immer mit einer 302 (temporären) Umleitung, bevor Sie zu einer 301 (permanenten) Umleitung wechseln, um Caching-Probleme zu vermeiden.
- (Wäre es für NextJS nicht besser,
article\index.html
eine Datei anstelle einer Datei im Stammverzeichnis zu erstellen ?)
Ja! Und Next kann das für Sie tun :
Es ist möglich , Next.js Export Seiten als index.html Dateien und erfordern folgende Schrägstriche zu konfigurieren,
/about
wird/about/index.html
und wird geroutet über/about/
. Dies war das Standardverhalten vor Next.js 9.Öffnen Sie
next.config.js
dieexportTrailingSlash
Konfiguration und aktivieren Sie sie, um zurückzuschalten und einen abschließenden Schrägstrich hinzuzufügen :
module.exports = { exportTrailingSlash: true, }