Nginx; ответный код ответа, если файл существует только с использованием файлов try_files

Так как IfIsEvil Я пытался настроить конфигурацию с помощью директивы try_files только для того, чтобы страница обслуживания отображается вместе с кодом ответа 503 для любого URI без исключения, то есть включая страницы php, если существует файл обслуживания.

В моей конфигурации есть две проблемы:

  • Страница обслуживания не отображается для URI php.
  • Код ответа 503 не возвращается, если файл maintenance.html существует.

Я видел похожие вопросы [1], [2] но не с решением, которое использует только try_files (в отличие от использования директивы if) и безоговорочно обслуживает страницу обслуживания с кодом ответа 503, если присутствует соответствующий файл. Возможно ли такое решение?

Ниже мой текущий неработающий файл conf. Он не содержит настройки кода ответа 503, потому что я не понимаю, куда он должен идти, чтобы он работал, как описано выше.

worker_processes            1;

error_log                   /var/log/nginx/error.log debug;

events {
    worker_connections      1024;
}

http {
    include                 mime.types;
    default_type            application/octet-stream;

    index                   index.php index.html index.htm;

    server {
        listen                  80;
        server_name             rpi;
        root                    /www;

        location / {
            try_files           /maintenance.html $uri $uri/ /index.php?$args;

            # pass the PHP скриптs to FastCGI server
            location ~ \.php$ {
                try_files           $uri =404;
                fastcgi_pass        unix:/run/php-fpm/php-fpm.sock;
                fastcgi_index       index.php;
                include             fastcgi.conf;
            }
        }
    }
}

Я предполагаю, что мой вопрос можно альтернативно сформулировать следующим образом: может ли try_files работать как структура управления if? Если это не само по себе, может ли оно с указанной целью быть объединено с другими директивами, чтобы действовать как таковое, исключая директиву if?

edit: Ниже приведено решение с использованием if, которое я сейчас использую, включив его в раздел сервера:

error_page              503 @maintenance;

if (-f $document_root/maintenance.html) {
        return          503;
}

location @maintenance {
        try_files       /maintenance.html =404;
}

Ответ 1

Очень хороший вопрос! Но это вообще невозможно, если вы не вызываете какой-то script, который устанавливает правильный код ответа.

Рабочее решение только с nginx и no if

Директива try_files выполняет только внутреннее перенаправление для последнего оператора. Но мы можем объединить его с директивой index и заставить внутреннее перенаправление.

# This will be the HTML file to display for the 503 error.
error_page 503 /maintenance/maintenance.html;

# Let nginx know that this particular file is only for internal redirects.
location = /maintenance/maintenance.html {
  internal;
}

# Any request that starts with the maintenance folder is 503!
location ^~ /maintenance/ {
  return 503;
}

# Instead of checking if a file exists and directly delivering it we check
# if a certain directory exists and trigger our index directive which will
# perform an internal redirect for us.
location / {
  expires epoch;
  try_files /maintenance/ $uri $uri/ /index.php?$args;
}

Любые недостатки с этим методом?

  • Фактически 301 перенаправляется в каталог вместо того, чтобы оставаться на одном и том же URL (поисковые системы).
  • Браузерное кэширование 301 перенаправления может быть проблемой, поэтому я добавил expires epoch в блок местоположения.

Другие решения?

Файл конфигурации nginx

Вместо создания файла HTML, почему бы не создать файл конфигурации nginx и просто перезагрузить процесс?

  • Намного лучше!
  • Легче понять!
  • Никаких побочных эффектов!

Конфигурация nginx может выглядеть следующим образом (обратите внимание, что этот if вовсе не злой):

error_page 503 /maintenance.html;

location / {
  include maintenance.conf;
  if ($maintenance = 1) {
    return 503;
  }
  try_files $uri $uri/ /index.php?$args;
}

Содержимое файла maintenance.conf:

set $maintenance 0;

И если вы хотите активировать режим обслуживания (в своей оболочке):

echo set $maintenance 1;> maintenance.conf && service nginx reload

Более продвинутый для друзей оболочки Вы могли бы даже расширить init script с помощью этого, например мой LSB-совместимый, заменив следующий блок в конце файла:

*)
  echo "Usage: ${NAME} {force-reload|reload|restart|start|status|stop}" >&2
  exit 1
;;

Со следующим блоком:

maintenance)
  echo "set $maintenance 1;" > /etc/nginx/maintenance.conf && service nginx reload;
;;

production)
  echo "set $maintenance 0;" > /etc/nginx/maintenance.conf && service nginx reload;
;;

*)
  echo "Usage: ${NAME} {force-reload|reload|restart|start|status|stop|maintenance|production}" >&2
  exit 1
;;

И теперь вы можете просто выполнить следующую команду (включая автозаполнение), чтобы перейти в режим обслуживания:

service nginx maintenance

Или вернуться в производство:

service nginx production

С файлом script/PHP

Другим чрезвычайно простым подходом, который будет работать как шарм, является использование файла PHP, который его обрабатывает.

location / {
    try_files /maintenance.php $uri $uri/ /index.php?$args;
}

Ваш PHP файл будет выглядеть точно так же, как ваш HTML файл, вам нужно только добавить к нему начало (предполагая PHP 5.4 +):

<?php http_response_code(503) ?><!doctype html>
<html>
<head>
<!-- ... more html ... -->