Обновлено:

Nginx try_files

try_files — одна из главных директив для статического сайта.
Она говорит Nginx: “проверь эти пути по порядку и отдай первый существующий файл”.

Для статической сборки Astro обычно нужен простой и предсказуемый вариант.

Базовый вариант

location / {
    try_files $uri $uri/ =404;
}

Что делает эта строка:

  1. Проверяет точный путь $uri.
  2. Проверяет директорию $uri/.
  3. Если ничего не найдено — отдаёт 404.

Почему работает /ru/docs/

При trailingSlash: 'always' Astro создаёт страницы как директории с index.html:

dist/ru/docs/index.html
dist/ru/docs/cache-control/index.html

Когда пользователь открывает:

/ru/docs/cache-control/

Nginx проверяет директорию и отдаёт:

/var/www/getsrv.app/ru/docs/cache-control/index.html

Что будет с /ru/docs/cache-control

Если запрос пришёл без trailing slash:

/ru/docs/cache-control

try_files $uri $uri/ =404 сначала проверит файл /ru/docs/cache-control, потом директорию /ru/docs/cache-control/.

Если директория есть, Nginx сможет отдать index.html.

Controlled 404

Для статического сайта лучше иметь управляемую 404-страницу:

error_page 404 /404.html;

location = /404.html {
    internal;
}

Тогда несуществующий путь:

curl -kI https://getsrv.app/no-such-page

вернёт:

HTTP/2 404
content-type: text/html

Это лучше, чем случайный directory listing или голая Nginx-страница.

Assets лучше проверять отдельно

Для /assets/ обычно делают отдельный location:

location /assets/ {
    try_files $uri =404;
    access_log off;
    add_header Cache-Control "public, max-age=31536000, immutable" always;
}

Здесь не нужен $uri/, потому что assets — это конкретные файлы, а не страницы-директории.

Плохой вариант для этого сайта

location / {
    try_files $uri /index.html;
}

Такой вариант часто используют для SPA. Для обычного статического справочника он хуже: несуществующие страницы могут отдавать главную с кодом 200.

Это ломает:

  • нормальный 404;
  • sitemap/SEO;
  • диагностику;
  • ожидания пользователя.

Проверить конкретную страницу

sudo test -f /var/www/getsrv.app/ru/docs/cache-control/index.html && echo OK || echo missing
curl -kI https://getsrv.app/ru/docs/cache-control/

Ожидаемо:

OK
HTTP/2 200

Проверить 404

curl -kI https://getsrv.app/no-such-page

Ожидаемо:

HTTP/2 404

Проверить assets

ASSET="$(find /var/www/getsrv.app/assets -maxdepth 1 -type f | head -n 1 | sed 's#^/var/www/getsrv.app##')"
curl -kI "https://getsrv.app$ASSET"

Ожидаемо:

HTTP/2 200
Cache-Control: public, max-age=31536000, immutable

Частые ошибки

Ошибка 1. SPA fallback вместо 404

try_files $uri /index.html;

Для справочника лучше не использовать.

Ошибка 2. Нет error_page 404

Тогда разные 404 могут выглядеть непредсказуемо.

Ошибка 3. Assets проходят через общий location

Если /assets/ не выделен отдельно, к ним может примениться HTML cache policy.

Ошибка 4. Web-root смотрит не туда

Проверьте:

sudo grep -RIn "root " /etc/nginx/sites-enabled /etc/nginx/conf.d

И убедитесь, что root указывает на актуальный каталог:

/var/www/getsrv.app

Минимальный рабочий набор

root /var/www/getsrv.app;

location /assets/ {
    try_files $uri =404;
    add_header Cache-Control "public, max-age=31536000, immutable" always;
}

location / {
    try_files $uri $uri/ =404;
    add_header Cache-Control "public, must-revalidate" always;
}

error_page 404 /404.html;

location = /404.html {
    internal;
}

Для обычной статической сборки этого достаточно.