Тщеславные URL-адреса без косой черты в конце на Nginx

С помощью @jon-lin мы выяснили, как удалить конечные косые черты в Apache (Тщеславные URL-адреса без завершающей косой черты в Apache); теперь, когда мы планируем запустить наш сайт на Nginx, мы хотели бы сделать то же самое. Мы попробовали несколько предложений, которые нашли в stackoverflow и других источниках, но все они приводят к бесконечному циклу (вероятно, потому, что браузеры пытаются вернуть косую черту). Наша текущая конфигурация (с тремя нашими попытками — rewrite ^/(.*)/$ https://www.example.com/$1 permanent; rewrite ^/(.*)/$ https://www.example.com/$1; и rewrite ^/(.*)/$ /$1 permanent; — закомментированы):

server {
        listen 80;
        server_name  example.com;
        return 301 https://www.example.com$request_uri;
}
server {
        listen 80;
        server_name  www.example.com;
        return 301 https://www.example.com$request_uri;
}
server {
        listen 443;
        server_name  example.com;
        return 301 https://www.example.com$request_uri;
}

server {
        listen 443 ssl default_server;
        listen [::]:443 ssl default_server;
        server_name www.example.com;

        #rewrite ^/(.*)/$ https://www.example.com/$1 permanent;
        #rewrite ^/(.*)/$ https://www.example.com/$1;
        #rewrite ^/(.*)/$ /$1 permanent;

        root /var/www/example.com/html;
        index index.php index.html index.htm;

        location / {
                # First attempt to serve request as file, then
                # as directory, then fall back to displaying a 404.
                try_files $uri $uri/ =404;
        }

        location ~ \.php$ {
                include snippets/fastcgi-php.conf;
                fastcgi_pass unix:/var/run/php5-fpm.sock;
        }

        ssl on;
        ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
        ssl_prefer_server_ciphers on;
        ssl_session_cache shared:SSL:10m;
        ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
        ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECD$
        add_header Strict-Transport-Security "max-age=63072000; includeSubdomains; preload";
        ssl_dhparam /etc/ssl/certs/dhparam.pem;

}

Как это может быть сделано?

В нашем конкретном случае нам нужно, чтобы все URL-адреса были лишены косой черты в конце; мы достигли этого, добавив следующее в httpd.conf Apache:

DirectorySlash Off

RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^(.*[^/])$ /$1/

Затем переписывание всех записей в /profiles/ было выполнено следующим образом:

RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ /profiles/$1 [NC,L]

person haadaa    schedule 05.12.2015    source источник


Ответы (2)


Если вы больше не используете каталог /profiles/, это должно работать:

server {
  ...
  root /var/www/example.com/html;
  index index.php index.html index.htm;

  location / {
    try_files $uri @rewrite;
  }
  location @rewrite {   
    rewrite ^(.*[^/])$ $1/; 
  }
  location ~ \.php$ { ... }
}

В приведенной выше конфигурации мы избегаем элемента $uri/ в try_files, так как он запускает перенаправление, которое добавляет конечный / к текущему URL-адресу.

Если вам нужна конфигурация, похожая на вашу предыдущую конфигурацию Apache, это должно работать:

server {
  ...
  root /var/www/example.com/html;
  index index.php index.html index.htm;

  location / {
    try_files $uri $uri/ @rewrite;
  }
  location @rewrite {   
    rewrite ^(.*[^/])$ /profiles$1/; 
  }
  location ~ \.php$ { ... }
}

Как видите, элемент $uri/ в этой ситуации не навредит.

Следуя вашим комментариям и недавнему редактированию, эта третья (менее элегантная) конфигурация сочетает в себе функции двух других и является близкой (но не точной) реализацией вашей конфигурации Apache:

server {
  ...
  root /var/www/example.com/html;
  index index.php index.html index.htm;

  location = / { rewrite ^ /index.php; }
  location / {
    try_files $uri @rewrite;
  }
  location @rewrite {
    if (-d $document_root/profile$uri) { rewrite ^ /profiles$uri/ last; }
    if (-d $request_filename) { rewrite ^(.*[^/])$ $uri/ last; }
  }
  location ~ \.php$ { ... }
}

И, наконец, эта версия немного отличается от брифа. Блок выполнения php заменяется именованным местоположением. Но это позволяет использовать каскадные директивы try_files, удаляя неприятные директивы if и отбрасывая index в процессе. Наслаждаться:

server {
  ...
  root /var/www/example.com/html;

  location / {
    try_files $uri $uri.html $uri/index.html /profiles$uri/index.html @php;
  }
  location @php {
    try_files $uri.php $uri/index.php /profiles$uri/index.php =404;
    include snippets/fastcgi-php.conf;
    fastcgi_pass unix:/var/run/php5-fpm.sock;
  }
  location ~ \.php$ { rewrite ^(.*)\.php$ $1 permanent; }
}

Это гибрид php без косой черты и без расширений, с настройкой ваших профилей.

person Richard Smith    schedule 05.12.2015
comment
Спасибо, @richard-smith! Ваш первый вариант (без /profiles/) сработал отлично! Второй — нет, то есть Nginx отказывался перезапускаться, если мы по какой-то причине оставляли там $url/. Но у нас работает первый вариант, а значит вы его действительно решили! Спасибо еще раз! - person haadaa; 06.12.2015
comment
Привет @richard-smith -- похоже, $url/ была опечатка -- когда мы изменили это на $uri/, ваш второй вариант тоже сработал. Что на самом деле очень круто; есть ли способ объединить оба, чтобы нигде не было косых черт? - person haadaa; 06.12.2015
comment
@haadaa Не уверен, что вы подразумеваете под сочетанием обоих. Вариант №1 уже работает на любом уровне иерархии. Исправлена ​​опечатка в адресе, спасибо. - person Richard Smith; 06.12.2015
comment
Привет, @richard-smith! Ваш второй вариант укорачивает URL-адреса в каталоге /profiles/, а также удаляет завершающие косые черты — профиль в example.com/profiles/profile1/index.php становится example.com/profile1 — именно то, что нам нужно. Но мы также хотели бы удалить завершающие косые черты в других частях сайта. Прямо сейчас, когда мы выбираем ваш второй вариант, example.com/noslash/ работает, а example.com/noslash возвращает 404. Мы хотели бы, чтобы example.com/noslash тоже работал. (Если, однако, мы выбираем ваш первый вариант, example.com/noslash работает, но example.com/profile1 возвращает 404.) - person haadaa; 06.12.2015
comment
@haadaa Проблема в том, что example.com/noslash перенаправляется на example.com/profiles/noslash/index.php, которого не существует. Что бы вы хотели, чтобы он делал? Прежде чем переписывать, проверьте наличие example.com/noslash.php и example.com/noslash/index.php? Это выполнимо, но, возможно, мне нужно немного подумать об этом :-) - person Richard Smith; 06.12.2015
comment
Привет, @richard-smith! Да, этого не должно происходить - должны быть перезаписаны только подкаталоги /profiles/. Все остальные страницы должны быть нетронутыми, за исключением того, что они также должны быть лишены завершающих косых черт. Я попытаюсь отредактировать вопрос и добавить код, который мы использовали в Apache. - person haadaa; 06.12.2015
comment
Привет, @richard-smith! Я добавил код, который мы использовали в Apache, к нашему вопросу выше. По сути, ваш первый вариант идеально подходит для нас — он лишает все на нашем сайте завершающих косых черт — именно то, что нам нужно. Затем нам (в дополнение или одновременно) также необходимо переписать подкаталоги profiles. Ваш второй вариант тоже делает это прекрасно, но в этом случае все оставшиеся страницы сохраняют косые черты, которые нам нужно удалить. - person haadaa; 06.12.2015
comment
@haadaa обновил ответ с третьим вариантом. Я немного протестировал его, но вы захотите проверить его более тщательно. - person Richard Smith; 06.12.2015
comment
Давайте продолжим обсуждение в чате. - person haadaa; 07.12.2015
comment
Привет, @richard-smith! Давайте продолжим говорить об этом в chat.stackoverflow.com/rooms/97139/ - person haadaa; 07.12.2015

Объединив первый вариант @richard-smith в его ответе здесь со «злом» if, мы заставили его работать:

    location / {
        if (!-e $request_filename) {
        rewrite ^(.*)$ /profiles$1/ break;
        }
        try_files $uri @rewrite;
    }

    location @rewrite {
        rewrite ^(.*[^/])$ $1/;
    }

Теперь все косые черты в конце исчезли, а профили в example.com/profiles/profile/ переписаны на example.com/profile.

person haadaa    schedule 07.12.2015