Я пытаюсь решить следующую задачу: вместо раздачи изображений в форматах png и jpeg необходимо предоставлять заранее созданные версии в форматах avif и webp, если браузер их поддерживает. Например, когда браузер запрашивает изображение `image.png`, если он поддерживает и avif, и webp, то сначала следует попытаться отдать `image.png.avif`. Если это изображение отсутствует, то нужно попробовать отдать `image.png.webp`. В случае отсутствия обеих альтернатив следует вернуть оригинал `image.png`. <br/> <br/> Основная логика такова: <br/> <br/> <pre><code>location ~* \.(png|jpe?g|gif)$ { set $avif_image_path ""; if ($http_accept ~* "image/avif") { set $avif_image_path "$uri.avif"; } set $webp_image_path ""; if ($http_accept ~* "image/webp") { set $webp_image_path "$uri.webp"; } add_header Vary Accept; try_files $avif_image_path $webp_image_path $uri =404; }</code></pre> <br/> <br/> Однако Nginx всегда возвращает оригинальный файл изображения. <br/> <br/> Я предоставляю полный листинг конфигурации и ссылку на репозиторий с примером: <a href="https://github.com/monochromer/nginx-image-test" rel="nofollow">https://github.com/monochromer/nginx-image-test</a>
Чтобы решить вашу задачу с использованием директивы `try_files` и переменных в Nginx, необходимо учесть несколько особенностей работы Nginx с переменными. Ваша проблема связана с тем, что переменные, которые устанавливаются внутри директивы `if`, не могут быть использованы в контексте `try_files` в Nginx. Директива `try_files` обрабатывает свои аргументы до того, как будут оценены переменные, что приводит к тому, что значения переменных, назначенные внутри `if`, не будут доступны. Для корректной работы с вашими условиями я рекомендую эмулировать поведение в одном из следующих подходов: ### Подход 1: Использование `map` Можно использовать директиву `map`, чтобы определить, какой именно файл пытаться отдавать, в зависимости от заголовка `Accept`: ```nginx map $http_accept $image_format { ~*image/avif $uri.avif; ~*image/webp $uri.webp; default $uri; } location ~* \.(png|jpe?g|gif)$ { add_header Vary Accept; try_files $image_format $uri =404; } ``` ### Подход 2: сократить использование директивы `if` Также можно заменить использование директивы `if` на более прямолинейный подход с использованием `try_files` в виде: ```nginx location ~* \.(png|jpe?g|gif)$ { add_header Vary Accept; # Первой проверяем поддержку AVIF try_files $uri.avif $uri.webp $uri =404; } ``` Но данный метод потребует правильного генерации файлов `image.png.avif`, `image.png.webp` заранее. ### Примечания 1. Убедитесь, что у вас есть соответствующие версии изображений (например, `image.png.avif` и `image.png.webp`) в директории, где хранятся оригинальные изображения. 2. Проверьте, чтобы ваши конфигурационные файлы были прогружены без ошибок, после того как вы внесли изменения. 3. Используйте `nginx -t`, чтобы проверить конфигурацию перед перезапуском сервера. Таким образом, предотвращая использование переменных в контексте `if`, вы сможете эффективно настроить Nginx для раздачи изображений в нужных форматах в зависимости от поддержки браузера.
Когда истинно уловиеif
исполняется только содержимое блока if.
И то что унаследовано от основной конфигурации, например,add_header
.
А вотtry_files
не наследуется и исполняется только когда уловиеif
ложно.
Эти проблемы возникают только в контекстеlocation
.
При использованииif
в контекстеserver
таких проблем нет.
Достаточно перенестиif
на уровеньserver
и всё заработает как ожидалось.
server { set $avif_image_path ""; if ($http_accept ~* "image/avif") { set $avif_image_path "$uri.avif"; } set $webp_image_path ""; if ($http_accept ~* "image/webp") { set $webp_image_path "$uri.webp"; } location ~* \.(png|jpe?g|gif)$ { add_header Vary Accept; try_files $avif_image_path $webp_image_path $uri =404; }
Или как советовали в комментариях избавиться отif
и переписать наmap
.