Как удалить завершающую косую черту из URL-адреса в nginx, только если каталог не существует?

Я запускаю сервер на nginx 1.4.1 с PHP-FastCGI. В настоящее время я настроил его так, чтобы он удалял конечные косые черты из моих URL-адресов и выполнял перенаправление 301. Однако, когда я захожу в существующий каталог, я попадаю в цикл перенаправления. Мой текущий корень документа выглядит так:

- index.php (app)
- webgrind
    - index.php
- static 
    - css

В настоящее время я не могу посетить example.com/webgrind или любой другой каталог. Мои журналы доступа неоднократно читались примерно так:

GET /webgrind/ HTTP/1.1" 301 178 "-"
GET /webgrind  HTTP/1.1" 301 178 "-"

Это блок сервера в моем nginx.conf:

server {
        listen 80;
        server_name example.com;

        location / {
            try_files $uri $uri/ /index.php?$args;
            root /var/www/example/public;
            index  index.php index.html index.htm;
        }

        rewrite ^/(.*)/$ /$1 permanent;

        location = /favicon.ico {
            access_log     off;
            log_not_found  off;
        }

        location ~ \.php$ {
            try_files $uri $uri/ /index.php?$args;
            root /var/www/example/public;
            index index.php index.html index.htm;

            fastcgi_pass   unix:/var/run/php5-fpm.sock;
            fastcgi_index  index.php;
            fastcgi_param  SCRIPT_FILENAME  /var/www/example/public$fastcgi_script_name;
            fastcgi_param  APPLICATION_ENV testing;
            fastcgi_param  PATH /usr/bin:/bin:/usr/sbin:/sbin;
            fastcgi_intercept_errors on;
            include        fastcgi_params;
        }
    }

Я знаю, что rewrite ^/(.*)/$ /$1 permanent; является оскорбительной строкой. Если я удалю его и зайду на example.com/webgrind, мне будет выдано сообщение 301 для перенаправления на example.com/webgrind/, поскольку это каталог. Однако мое приложение теперь будет принимать как конечные, так и неконечные косые черты (например, example.com/users/ и example.com/users), а это не то, что мне нужно.

Обертывание директивы 'if' вокруг моей перезаписи следующим образом по-прежнему создает цикл перенаправления для моих каталогов (if is evil , видимо, но директива rewrite в этом случае считается безопасной):

if (!-d $request_filename) {
    rewrite ^/(.*)/$ /$1 permanent;
}

(Я знаю, что посещение webgrind/index.php решит мою проблему, но я хотел бы избежать дорогостоящих и непрофессиональных циклов перенаправления, когда мои производственные каталоги загружаются в реальном времени.)

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

ОБНОВЛЕНИЕ: Моя (неизменная) конфигурация fastcgi_params:

fastcgi_param   QUERY_STRING            $query_string;
fastcgi_param   REQUEST_METHOD          $request_method;
fastcgi_param   CONTENT_TYPE            $content_type;
fastcgi_param   CONTENT_LENGTH          $content_length;

fastcgi_param   SCRIPT_FILENAME         $request_filename;
fastcgi_param   SCRIPT_NAME             $fastcgi_script_name;
fastcgi_param   REQUEST_URI             $request_uri;
fastcgi_param   DOCUMENT_URI            $document_uri;
fastcgi_param   DOCUMENT_ROOT           $document_root;
fastcgi_param   SERVER_PROTOCOL         $server_protocol;

fastcgi_param   GATEWAY_INTERFACE       CGI/1.1;
fastcgi_param   SERVER_SOFTWARE         nginx/$nginx_version;

fastcgi_param   REMOTE_ADDR             $remote_addr;
fastcgi_param   REMOTE_PORT             $remote_port;
fastcgi_param   SERVER_ADDR             $server_addr;
fastcgi_param   SERVER_PORT             $server_port;
fastcgi_param   SERVER_NAME             $server_name;

fastcgi_param   HTTPS                   $https;

# PHP only, required if PHP was built with --enable-force-cgi-redirect
fastcgi_param   REDIRECT_STATUS         200;

person danronmoon    schedule 24.08.2013    source источник
comment
Пара вещей: 1) я не уверен, что полностью понимаю, чего вы пытаетесь достичь с точки зрения поведения для удаления завершающей косой черты - вы пытаетесь добиться перенаправления на уровне сервера, где, если клиент отправляет foo.com/aaa или foo.com/aaa/, что сервер ответит документом; или вы пытаетесь заставить сервер сообщить браузеру о перенаправлении с foo.com/aaa/ на foo.com/aaa?   -  person chue x    schedule 02.11.2013
comment
2) Я не могу воспроизвести цикл перенаправления, если использую блок if. Вы можете столкнуться с проблемой, если используете старый браузер — см. Internet Explorer 9 постоянно кэширует перенаправления. Так что это означает, что даже с блоком if IE попытается перенаправить, поскольку он кэшировал старую перезапись. Чтобы решить эту проблему, вам нужно очистить кеш браузера.   -  person chue x    schedule 02.11.2013
comment
@chuex Я пытаюсь заставить сервер сообщить клиенту о перенаправлении с foo.com/aaa/ на foo.com/aaa с ответом 301. Я протестировал последние версии Chrome и Firefox на 2 разных компьютерах и очистил кеш, но эта проблема все еще возникает.   -  person danronmoon    schedule 03.11.2013
comment
С блоком if тоже не могу воспроизвести... Есть записи в nginx error.log? А может быть, ваше приложение случайно перенаправляет wrongfolder на wrongfolder/ (тем самым создавая сам бесконечный цикл, борясь с nginx)?   -  person rchukh    schedule 03.11.2013
comment
@rchukh В журнале ошибок nginx нет записей о цикле перенаправления, и я не удивлен, потому что 301 - это нормальные операции. Я не уверен, что вы имеете в виду под wrongfolder... Я на 100% уверен, что эти папки существуют, если вы об этом спрашиваете. Если бы каталога не существовало, мое приложение (index.php) позаботилось бы о маршрутизации (это то, что делает /index.php?$args). Теперь я чувствую себя лучше, так как есть 2 случая работы блока if, поэтому, возможно, моя производственная установка решит эту проблему. Не могли бы вы скинуть ссылку на ваш тестовый конфиг? Я могу опубликовать свой http-блок, но не уверен, что это поможет   -  person danronmoon    schedule 03.11.2013
comment
Под wrongfolder я имел в виду какую-то папку, которой нет на вашем сервере, например. пользователь запрашивает ip:8080/wrongfolder/ и nginx (зная, что этой папки не существует, из-за этого if) перенаправляет на ip:8080/wrongfolder, который будет обрабатываться index.php. Я хотел спросить, перенаправляет ли index.php обратно на ip:8080/wrongfolder/... по каким-то неизвестным (мне) причинам. Довольно странный случай, но я не знаю, что внутри index.php...   -  person rchukh    schedule 03.11.2013
comment
В любом случае - вот полный суть nginx.conf, который работает на моей стороне (я заменил php-fpm часть с empty_gif, чтобы избежать возможных проблем с вашей стороны, связанных с index.php).   -  person rchukh    schedule 03.11.2013
comment
@rchukh index.php — это public/index в Zend Framework 1. .php, созданный с помощью Zend_Tool. Я немного поиграюсь с конфигурацией и вернусь к вам, спасибо.   -  person danronmoon    schedule 03.11.2013


Ответы (1)


Помещение директивы root вне блока location в качестве прямого потомка блока server устранило проблему.

server {
    listen 80;
    server_name example.com;

    # This WORKS!
    root /var/www/example/public; 

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

    if (!-d $request_filename) {
        rewrite ^/(.*)/$ /$1 permanent;
    }

    location = /favicon.ico {
        access_log     off;
        log_not_found  off;
    }

    location ~ \.php$ {
        try_files $uri $uri/ /index.php?$args;
        index index.php index.html index.htm;

        fastcgi_pass   unix:/var/run/php5-fpm.sock;
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME  /var/www/example/public$fastcgi_script_name;
        fastcgi_param  APPLICATION_ENV testing;
        fastcgi_param  PATH /usr/bin:/bin:/usr/sbin:/sbin;
        fastcgi_intercept_errors on;
        include        fastcgi_params;
    }
}

По-видимому, это ловушка, которую вики Nginx рекомендует избегать.

person danronmoon    schedule 03.11.2013
comment
Просто чтобы расширить ваш ответ - ловушка с наличием корневой директивы внутри блока местоположения заключается в том, что другие блоки местоположения не имеют корневой директивы. (Если у них нет собственных дублирующих корневых директив.) Если корневая директива находится на уровне сервера, то она применяется ко всем блокам расположения. - person chue x; 03.11.2013
comment
Я считаю, что пункт if, проверяющий каталог, был единственным, чтобы решить проблему… - person Daniel; 03.02.2015