Перейти к содержанию

Nginx: статика, 404 и старые URL

У учебного VPS появился издатель, а артефакт находится отдельно от исходников. Теперь свяжем запросы с его публичной частью. Особенность нашего примера — старый адрес /my/legacy/first.php: этот файл содержит готовый HTML. Сервер должен отдать его как страницу, не пытаясь выполнить PHP и не направляя посетителя на другой путь.

Рассматриваем изолированный статический виртуальный сайт Nginx на Ubuntu. Приведённая конфигурация не подходит для каталога, где лежит настоящий исходный PHP-код: выдача такого кода как текста раскрыла бы приложение. В пятнадцатом уроке настоящий PHP получит отдельный корень и отдельный обработчик. Здесь смешивать два режима не требуется.

Корень и выбор файла

Постоянный корень зададим как /srv/release-lab/current/public. Ссылка current позднее укажет на один из полностью подготовленных выпусков. URL /catalog/ должен находить catalog/index.html, а старый путь — соответствующий файл внутри my/legacy. Префикс /srv/release-lab не является частью публичного адреса: его добавляет сервер при поиске содержимого.

server {
    listen 80;
    server_name example.com;
    root /srv/release-lab/current/public;
    index index.html;

    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    location ~ \.php$ {
        types { text/html php; }
        try_files $uri =404;
    }

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

    error_page 404 /404.html;
    location = /404.html { internal; }
    location = /health.json {
        add_header Cache-Control "no-store" always;
        try_files $uri =404;
    }

    access_log /var/log/nginx/release-lab.access.log;
    error_log /var/log/nginx/release-lab.error.log;
}

Это первоначальный HTTP-блок, который используется до подготовки TLS. После настройки сертификата обычные HTTP-запросы направляют на HTTPS с сохранением URI, а обслуживающие location размещают в TLS-блоке. Путь ACME согласуют с клиентом выпуска отдельно. Не нужно оставлять два самостоятельных сайта с разным содержимым просто потому, что оба протокола технически работают.

Директива try_files проверяет файл или каталог и возвращает 404, если они отсутствуют. В ней намеренно нет запасного перехода на главную. Для обычного многостраничного сайта такой переход превратил бы неизвестный адрес в страницу с кодом 200 и скрывал потерю старого материала. Режим приложения с клиентской маршрутизацией решает другую задачу и не должен переноситься сюда без анализа. Описание try_files в Nginx.

Тип содержимого старой страницы

В отдельном обработчике расширения .php задаём text/html. В нём нет fastcgi_pass: запрос не передаётся интерпретатору. Успешное открытие старого адреса ожидается в виде HTML-документа с обычным содержанием. Если браузер предлагает скачать файл, нужно проверить MIME-тип, а не менять URL статьи. Если появляется 502, запрос, вероятно, попал в конфигурацию приложения, которой у статического стенда не должно быть.

В существующей серверной конфигурации могут присутствовать другие регулярные location. Их порядок и приоритет влияют на выбранный обработчик. Перед включением нашего блока читатель изучает эффективную конфигурацию и не вставляет статический фрагмент внутрь сайта с готовой PHP-настройкой. Простота изолированного корня позволяет проверить режим выдачи по небольшому набору адресов, не рискуя смешать файлы разных назначений.

Ошибка с человеческой страницей

Посетитель неизвестного адреса должен получить полезное объяснение, но статус ответа остаётся 404. error_page выполняет внутренний переход к файлу оформления. В примере не используется конструкция =200, которая изменила бы статус. Директива internal запрещает прямое открытие /404.html, но позволяет серверу использовать её при внутренней обработке ошибки. Это сознательное отличие от исходного дерева, где файл просто существовал как отдельная страница. Документация error_page.

У будущей проверки есть два наблюдения. Запрос к /missing-for-check должен вернуть 404 и оформление ошибки. Прямой запрос к /404.html тоже будет недоступен извне при выбранном internal. Это не потеря обязательной страницы: служебный файл входит в артефакт ради обработки отсутствующих URL. Если проекту нужно прямое открытие оформления, internal можно убрать, сохранив корректный статус исходного неизвестного адреса.

Подготовка и чтение ответа

На Ubuntu после установки пакета обычно используют отдельный файл сайта и включение из sites-enabled. Имена каталогов и уже активные блоки проверяют на выбранной системе, особенно если сервер был подготовлен панелью. Не следует заменять всю конфигурацию ради одного виртуального сайта. Руководство Ubuntu по настройке Nginx.

sudo nginx -t
sudo systemctl reload nginx
curl --dump-header old.headers --output old.html \
  http://example.com/my/legacy/first.php
curl --dump-header missing.headers --output missing.html \
  http://example.com/missing-for-check

Здесь показаны будущие команды, их результаты не получены при написании. Разбор конфигурации проверяет синтаксис и часть доступности файлов настроек, но не содержание каждой статьи. После применения читатель сравнивает статус, тип содержимого и тело. Для старой страницы ожидаются 200 и нужный текст, для отсутствующей — 404. Проверка только HEAD может не повторить поведение GET во всех конфигурациях, поэтому для материала сохраняем полноценный ответ.

Что делать с разрешениями и ресурсами

Ошибка 403 часто связана с невозможностью прочитать файл или пройти через один из родительских каталогов. Даже верные права самого index.html не помогают, если процесс Nginx не может войти в /srv/release-lab/releases/r2. Проверяют весь путь и реальную учётную запись процесса. Выдавать всему дереву право записи для всех пользователей не требуется: веб-серверу статики достаточно чтения файлов и прохода по каталогам.

После страницы проверяют её стили, изображения и поисковый индекс. Новый корень может правильно отдавать HTML, но отсутствующий ресурс всё ещё даст 404. Пути, начинающиеся от корня сайта, сохраняют смысл при смене выпуска; относительные пути зависят от адреса страницы. Поэтому обязательный старый глубокий URL полезен как проверка не только самого файла, но и связанных ресурсов.

Есть ещё одно полезное наблюдение: неизвестное имя хоста не должно случайно показывать библиотеку только потому, что наш серверный блок оказался первым. Поведение сервера по умолчанию описывают отдельно от сайта example.com. В учебном стенде допустим самостоятельный блок, который отклоняет запросы для незнакомых имён. Это помогает заметить ошибку привязки домена и не создаёт лишние публичные копии по случайным именам. Однако такой блок не заменяет правильный сертификат и не применяется наугад к серверу с другими проектами. Сначала определяют все обслуживаемые имена, затем выбирают явное поведение для оставшихся запросов. Проверка старого URL должна обращаться к правильному имени, а не только к IP.

Теперь наш стенд имеет определённое поведение: существующие статические страницы отдаются напрямую, старые .php остаются HTML, неизвестные адреса получают настоящую ошибку. Следующий урок перенесёт упаковку готового дерева в GitLab CI, сохранив публикацию отдельным действием.