Richtige .htaccess-Konfiguration für Next.js SSG

Jul 09 2020

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/articlewird die Seite article.html angezeigt, in meiner Adressleiste wird jedoch domain/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/articleführt mich das manuelle Eingeben dazu, domain/article/anstatt article.htmlohne die .htmlErweiterung anzuzeigen.

Damit...

  • Wie behebe ich das?
  • Ist das ein .htaccess-Problem?
  • Ein nextjs Konfigurationsproblem?
  • (Wäre es für NextJS nicht besser, article\index.htmleine Datei anstelle einer Datei im Stammverzeichnis zu erstellen ?)

exportTrailingSlash

Ich habe versucht, damit herumzuspielen, exportTrailingSlashwas 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/somearticleauf "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 .htaccessRegeln, 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

6 MrWhite Jul 14 2020 at 05:30

Wenn Sie ein physisches Verzeichnis anfordern /articleund /articleals 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 /articleauf 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 /articledie 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 DirectoryIndexDokument 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/articledas 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 /foonach /foo.htmlODER /foonach /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 .htmlDatei überprüft, überprüft nicht unbedingt dieselbe Datei, in die neu geschrieben wird. z.B. Wenn Sie anfordern /foo/bar, wo /foo.htmlvorhanden, aber kein physisches Verzeichnis vorhanden ist, prüft /foodie RewriteCondDirektive 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, .jsund 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 /articleauf /article.htmlund /article/somearticlezu /article/somearticle.htmlmüß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.htmlSie 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 .htaccessDatei weitere Anweisungen hinzufügen . Zum Beispiel:

  1. Sie können die angeforderte URL oben in der Datei nach Dateierweiterungen durchsuchen, um eine weitere Verarbeitung zu verhindern. Die RewriteRuleRegex für jede nachfolgende Regel könnte dann "vereinfacht" werden.
  2. Anforderungen, die einen abschließenden Schrägstrich enthalten, können blockiert oder umgeleitet werden (um den abschließenden Schrägstrich zu entfernen).
  3. Wenn die Anforderung eine .htmlDatei ist, leiten Sie zur URL ohne Erweiterung um. Dies wird etwas komplizierter, wenn Sie mit beiden /foo.htmlund /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.

jdaz Jul 11 2020 at 04:00
  • (Wäre es für NextJS nicht besser, article\index.htmleine 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, /aboutwird /about/index.htmlund wird geroutet über /about/. Dies war das Standardverhalten vor Next.js 9.

Öffnen Sie next.config.jsdie exportTrailingSlashKonfiguration und aktivieren Sie sie, um zurückzuschalten und einen abschließenden Schrägstrich hinzuzufügen :

module.exports = { exportTrailingSlash: true, }