Next.jsSSGの適切な.htaccess設定

Jul 09 2020

NextJSは、次の構造で静的サイトをエクスポートします。


|-- index.html
|-- article.html
|-- tag.html
|-- article
|   |-- somearticle.html
|   \-- anotherarticle.html
\-- tag
    |-- tag1.html
    \-- tag2.html

.htaccessファイルを使用して.html拡張子を非表示にしています。

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

以下を除いて、すべてが完璧に機能します。

  • リンクをたどるとdomain/articlearticle.htmlページが表示されますが、アドレスバーにdomain/article<-Goodと表示されます。
  • 更新すると、次のアドレスに送信されます:(domain/article/末尾のスラッシュに注意)記事ディレクトリの内容を一覧表示します<-悪い(タグと同じ)
  • 同様に、手動で入力すると、拡張子なしで表示domain/articleするdomain/article/代わりにに移動します。article.html.html

そう...

  • これを修正するにはどうすればよいですか?
  • これは.htaccessの問題ですか?
  • nextjs構成の問題?
  • (NextJSarticle\index.htmlがルートディレクトリにファイルの代わりに作成する方が良いのではないでしょうか?)

exportTrailingSlash

exportTrailingSlash関連していると思われるものを試してみましたが、これにより、すべてのリンクの最後に常に末尾のスラッシュが表示されるなど、他の問題が発生しました。

例:行ってdomain/article/somearticle更新を押すと、何か(.httaccess?)が/末尾にを追加して、domain/article/somearticle/ひどくなく、あまりきれいで一貫性がないようにします...

編集:実際には、それはもう少し恐ろしいです。なぜなら、末尾にスラッシュが表示されることもあれば、nextjsリンクにアクセスしないこともあるからです...私がどのように使用しているかについての何かであるに違いありませんが、それを理解<Link />できません。

とにかく、.htaccess私が試したルールのどれも、毎回末尾のスラッシュを正常に削除することはできませんでした...


詳細:

次のアプリには、次のフォルダーがあります。

/articles/
   [slug].js
   index.js

さまざまなページで、nextJSLinkコンポーネントを使用しています。

import Link from 'next/link';

<Link href="/articles" as="/articles">
            <a>Articles</a>
</Link>

回答

6 MrWhite Jul 14 2020 at 05:30

あなたが要求した場合/article/article、Apacheののmod_dirその後、物理ディレクトリとして存在し、(デフォルトでは)「修正」させるためにURL末尾にスラッシュを追加します。これは、301の永続的なリダイレクトで実現されるため、ブラウザによってキャッシュされます。

ファイルと同じベース名の物理ディレクトリがあり、拡張子のないURLを使用すると、あいまいさが生じます。例えば。され/articleたディレクトリにアクセスすることになっ/article/たり、ファイルを/article.html。とにかくディレクトリへの直接アクセスを許可したくないようです。そのため、そのあいまいさは解決されるようです。

Apache mod_dirがディレクトリに末尾のスラッシュを追加しないようにするには、を無効にする必要がありDirectorySlashます。例えば:

DirectorySlash Off

ただし、前述のように、以前にアクセスしたことがある場合/articleは、リダイレクト先が/article/ブラウザによってキャッシュされているため、これを有効にするには、ブラウザのキャッシュをクリアする必要があります。

ファイル拡張子を削除するため、MultiViewsが無効になっていることも確認する必要があります。無効にしないと、mod_negotiationが基になるファイルの内部サブリクエストを発行し、mod_rewriteと競合する可能性があります。MultiViewsはデフォルトで無効になっていますが、一部の共有ホストでは何らかの理由で有効になっています。取得した出力からは、MultiViewsが有効になっているようには見えませんが、確認することをお勧めします...

# Ensure that MutliViews is disabled
Options -MultiViews

ただし、ディレクトリ自体にアクセスできるようにする必要がある場合は、内部の書き換えを使用して末尾のスラッシュを手動で追加する必要があります。これはここでは要件ではないようですが。ただし、ディレクトリリストが無効になっていることを確認する必要があります。

# Disable directory listings
Options -Indexes

(最終的にファイルにマップされない-以下を参照)任意のディレクトリにアクセスしようとして、DirectoryIndexドキュメントが含まれていない場合、ディレクトリリストではなく、403Forbidden応答が返されます。

リンクをたどるdomain/article、ページを更新する、手動で入力することの間に発生する可能性がある唯一の違いdomain/articleは、ブラウザまたは任意の中間プロキシキャッシュによるキャッシュであることに注意してください。(アンカーのクリックイベントをインターセプトするJavaScriptがない限り?!)

あなたはまだからの要求を書き直す必要がない/foo/foo.htmlOR/foo/foo/index.html(下記参照)、あなたのサイトを設定しているかによって異なります。両方ではなく、どちらか一方を選択することが望ましいですが(あなたが示唆しているように)。

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

キャッシュされた応答が表示されていない限り、これが現在どのように「機能している」ように見えるかは不明です。を要求/articleすると、これは物理ディレクトリとして存在し、ルールが処理されないため、最初の条件は失敗します。MultiViewsが有効になっている場合でも、mod_dirが優先され、末尾にスラッシュが追加されます。

.htmlファイルの存在をチェックする2番目の条件は、書き換えられる同じファイルを必ずしもチェックすることではありません。例えば。あなたが要求した場合は/foo/bar/foo.html存在しますが、物理的なディレクトリが存在しない/foo、その後RewriteCondの存在を指示チェック/foo.html-成功しているが、要求は内部に書き換える/foo/bar.html(キャプチャからRewriteRule パターン) -内部リライトループと500で、この結果クライアントに返されるエラー応答。ここで実際に起こっていることの背後にある詳細については、次のServerFaultの質問に対する私の回答を参照してください。

我々は、ファイルの拡張子どのようなものかを含む任意のURL(例。静的リソースと仮定した場合我々はまた、さらなる最適化を行うことができ.css.jsおよび画像ファイル)は無視されるべきであるそうでない場合、我々は比較的高価であるすべての要求、上のファイルシステムチェックを実行しています、 。

だから、フォームの(内部的に書き換え)要求マッピングするために/article/article.htmlして/article/somearticleまで/article/somearticle.html、あなたが好きなものを読むために上記のルールを変更する必要があります:

# Rewrite /foo to /foo.html if it exists
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI}.html -f
RewriteRule !\.\w{2,4}$ %{REQUEST_URI}.html [L]

RewriteCond TestStringのリテラルドットをバックスラッシュでエスケープする必要はありません。ドットはここでは特別な意味を持ちません。正規表現ではありません。

次に、/fooマップする必要のあるフォームのリクエストを処理するには/foo/index.html、次のようにします。

# 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]

通常、mod_dirがDirectoryIndex(例index.html)を提供することを許可しますが、ディレクトリから末尾のスラッシュを省略しているため、これは問題になる可能性があります。

概要

上記の点をまとめると、次のようになります。

# 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]

これは、サイトの構造や.htaccessファイルにディレクティブを追加するかどうかに応じて、さらに最適化できます。例えば:

  1. ファイルの先頭にある要求されたURLでファイル拡張子を確認して、それ以上の処理を防ぐことができます。RewriteRuleその後、後続の各ルールの正規表現を「簡略化」できます。
  2. 末尾のスラッシュを含むリクエストは、ブロックまたはリダイレクトされる可能性があります(末尾のスラッシュを削除するため)。
  3. リクエストが.htmlファイルに対するものである場合は、拡張子のないURLにリダイレクトします。との両方/foo.htmlを扱っている場合、これは少し複雑になり/foo/index.htmlます。ただし、これは、既存のURL構造を変更する場合にのみ本当に必要です。

たとえば、上記の#1と#2を実装すると、ディレクティブを次のように記述できます。

# 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]

キャッシュの問題を回避するために、301(永続的)リダイレクトに変更する前に、常に302(一時的)リダイレクトでテストしてください。

jdaz Jul 11 2020 at 04:00
  • (NextJSarticle\index.htmlがルートディレクトリにファイルの代わりに作成する方が良いのではないでしょうか?)

はい!そしてNextはあなたのためにそれをすることができます:

Next.jsを構成して、ページをindex.htmlファイルとしてエクスポートし、末尾にスラッシュを付ける必要があります。これは、を介してルーティング可能に/aboutなります。これは、Next.js9より前のデフォルトの動作でした。/about/index.html/about/

元に戻して末尾のスラッシュを追加するには、構成を開いnext.config.jsて有効にしexportTrailingSlashます。

module.exports = { exportTrailingSlash: true, }