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 не буде т.к. з ним не працював так багато. А от якщо ви працювали, можете доповнити цю статтю, написавши мені.

О, а ви бачили мій секрет?

Мирного неба вам!