Hammerill Light Logo

Слэши на конце в nginx

Слэши на конце в nginx

5 июня 2024 г.

Используя nginx для хостинга простого HTML, есть вероятность наткнуться на частую проблему: может понадобиться заставить URI заканчиваться на слэш или, наоборот, убрать его. Иначе, сайт может работать неправильно и даже пугать пользователя экраном ошибки.

Например, вы сделали статический экспорт Next.js с параметром "без слэшей на конце", так что пользователь, по идее, должен открывать "/page". Таким образом, вы можете хостить сайт с помощью такой директивы: try_files $uri $uri.html $uri/ =404;.

Но вдруг, открывая "/page/" мы видим ошибку 404! (или 403)

Такого нужно избежать в любом случае. Сейчас узнаете как.

(Да. Я написал целую статью об этом. Но здесь со мной можно поучить regex!)

Добавить слэш в URI

Дабы заставить URI заканчиваться слэшом в nginx, используйте эту директиву:

# Добавить слэш в конце (напр. ".../page" -> ".../page/")
location ~ ^(.+[^/])$ {
    return 301 $scheme://$host$1/;
}

Эта директива захватывает все URI попадающие в regex ^(.+[^/])$ (выглядит ужасно непонятно, но сейчас объясню):

  • ^ и $ означают начало и конец URI;
  • (), или же скобки, означают сохранение значения внутри них в "группу regex". Так как это первая (и единственная) группа, она доступна в nginx как переменная $1;
  • .+ означает строку из любых символов любой длины (проще говоря, почти весь URI);
  • а [^/] означает любой символ не являющимся слэшем. В nginx его не надо перекрывать вот так: [^\/] (а в обычном regex надо).

Таким образом, все запросы на URI не заканчивающиеся на слэш попадут внутрь и запустят директиву "return 301", сообщая чётко клиенту о том, что страница имеет другой вид URL (с оригинальным URI полученным через группу regex 1 или $1 + добавленный слэш в конце).

Напоминаю, ответственность за вашу маршрутизацию несёте вы! Особенно корень, /. Этот regex можно подправить (например, использовать .* вместо .+ дабы захватывать ещё те случаи, когда слева от слэша ничего нет. Опять же, это вам надо всё посмотреть и проверить).

Эта техника базируется на "redirect", т.е. перенаправлении. Можно вместо неё использовать "rewrite" (способ ищется в сети). Я просто даю пример того, что лично считаю более корректным.

Теперь, открывая "/page" мы попадаем в "/page/". Открывать "/page/" напрямую ничего не даёт и страница просто загружается.

Убрать слэш из URI

Дабы заставить URI не заканчиваться слэшом в nginx, используйте эту директиву:

# Убрать слэш в конце (напр. ".../page/" -> ".../page")
location ~ ^(.+)/$ {
    return 301 $scheme://$host$1;
}

Эта директива захватывает все URI попадающие в regex ^(.+)/$:

  • ^ и $ означают начало и конец URI;
  • (.+) означает строку из любых символов любой длины (проще говоря, почти весь URI). Вложив точку (любой символ) и плюс (любое кол-во раз с 1) в скобки, мы сохраняем значение внутри них в группу regex №1, или же переменную $1 в nginx;
  • а / это слэш, на который мы ведём охоту. В nginx его не надо перекрывать вот так: \/. Обратите внимание на то, что он вне скобок. Так он не входит в группу regex №1.

Таким образом, все запросы на URI заканчивающиеся на слэш попадут внутрь и запустят директиву "return 301", сообщая чётко клиенту о том, что страница имеет другой вид URL (с URI без слэша полученным благодаря regex в группе №1 как переменная $1).

В конце концов, общая картина блока "server" может выглядить вот так:

...
root /var/www/html/;
index index.html;

# Убрать слэш в конце (напр. ".../page/" -> ".../page")
location ~ ^(.+)/$ {
    return 301 $scheme://$host$1;
}

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

А теперь, открывая "/page" мы попадаем на страницу напрямую. Открытие "/page/" нас перекинет на "/page", таким образом, например, спасая нас от ошибки 404 рассказанной в самом начале.

Извините, гайда по Apache не будет т.к. с ним не работал так много. А вот если вы работали, можете дополнить эту статью написав мне.

О, а вы видели мой секрет?

До скорого!