Next.js SSG에 대한 적절한 .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/article가면 article.html 페이지가 표시되지만 주소 표시 줄에 domain/article<-Good.
  • 새로 고침하면 다음 주소로 전송됩니다 : domain/article/(후행 슬래시 참고) 기사 디렉토리의 내용을 나열하는 <-Bad (태그와 동일)
  • 마찬가지로 수동으로 입력 하면 확장자 없이 표시 domain/article되는 domain/article/대신로 이동 합니다.article.html.html

그래서...

  • 이 문제를 어떻게 해결합니까?
  • .htaccess 문제입니까?
  • nextjs 구성 문제?
  • (NextJS가 article\index.html루트 디렉토리에 파일 대신 파일 을 만드는 것이 더 낫지 않을까요 ?)

exportTrailingSlash

exportTrailingSlash관련 되어 있는 것처럼 보이지만 이로 인해 모든 링크 끝에 항상 슬래시가있는 것과 같은 다른 문제가 발생했습니다.

예 : 내가 가서 domain/article/somearticle새로 고침을 누르면 (.httaccess?) 무언가 (.httaccess?)가 /끝에 a 를 추가하여 domain/article/somearticle/끔찍하지 않고 매우 깨끗하고 일관성이 없습니다 ...

편집 : 사실, 그것은 조금 더 끔찍합니다. 때로는 후행 슬래시가 생기고 때로는 nextjs 링크가 없기 때문에 ... 내가 사용하는 방법에 대한 무언가가 있어야<Link />하지만 그것을 알아낼 수는 없습니다.

그럼에도 불구하고 .htaccess내가 시도한 규칙 중 어느 것도 매번 후행 슬래시를 성공적으로 제거하지 못했습니다.


자세한 내용은:

다음 앱에는 폴더가 있습니다.

/articles/
   [slug].js
   index.js

다양한 페이지에서 nextJS Link 컴포넌트를 사용합니다.

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는 기본적으로 비활성화되어 있지만 일부 공유 호스트는 어떤 이유로이를 활성화합니다. 출력에서 MultiView가 활성화 된 것처럼 보이지는 않지만 확인하는 것이 좋습니다.

# Ensure that MutliViews is disabled
Options -MultiViews

그러나 디렉토리 자체에 액세스 할 수 있어야하는 경우 내부 재 작성과 함께 후행 슬래시를 수동으로 추가해야합니다. 이것은 여기서 요구 사항이 아닌 것 같습니다. 그러나 디렉토리 목록이 비활성화되어 있는지 확인해야합니다.

# Disable directory listings
Options -Indexes

파일에 매핑되지 않고 DirectoryIndex문서를 포함하지 않는 디렉터리에 액세스하려고 하면 디렉터리 목록 대신 403 Forbidden 응답이 반환됩니다.

에 대한 링크를 사이에 발생할 수있는 유일한 차이점 것을 참고 domain/article, 페이지를 새로 고침 및 수동 입력이 domain/article입니다 ... 캐싱 브라우저 또는 중간 프록시 캐시에 의해 하나. (앵커에서 클릭 이벤트를 가로채는 JavaScript가 없다면?!)

당신은 여전히에서 요청을 다시 작성해야합니까 /foo/foo.html/foo/foo/index.html(아래 참조), 당신이 당신의 위치를 구성한 방법에 따라. 둘 다 선택하는 것이 아니라 둘 중 하나를 선택하는 것이 더 나을 수 있지만 (그럴 수 있음을 암시하는 것처럼).

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

캐시 된 응답이 표시되지 않는 한 이것이 현재 "작동"하는 것처럼 보이는 방법이 확실하지 않습니다. 을 요청할 때 /article첫 번째 조건은 물리적 디렉터리로 존재하고 규칙이 처리되지 않기 때문에 실패합니다. MultiViews가 활성화 된 경우에도 mod_dir은 우선 순위를 가지며 후행 슬래시를 추가합니다.

.html파일 의 존재를 확인하는 두 번째 조건 은 반드시 다시 작성되는 동일한 파일을 확인하는 것은 아닙니다. 예. 요청 /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
  • (NextJS가 article\index.html루트 디렉토리에 파일 대신 파일 을 만드는 것이 더 낫지 않을까요 ?)

예! 다음 은 당신을 위해 할 수 있습니다.

후행 슬래시를 index.html을 파일로 내보내기 페이지에 Next.js을 구성하고 필요로하는 것이 가능 /about하게 /about/index.html하고 통해 라우팅입니다 /about/. 이것은 Next.js 9 이전의 기본 동작이었습니다.

다시 전환하고 후행 슬래시를 추가하려면 구성을 열고 next.config.js활성화합니다 exportTrailingSlash.

module.exports = { exportTrailingSlash: true, }