301 redirect на URI без слэша на конце

Андрей Середенко andrei.seredenko at gmail.com
Thu Jan 9 14:55:57 UTC 2014


Здравия, друзья! Всех с прошедшими праздниками!

В процессе приведения конфигурации своих веб-серверов в более лицеприятный
столкнулся с таким моментом: автоматическое добавление слэша не происходит
после отрабатывания директивы try_files. Было неожиданно. Немного примеров,
чтобы стало понятнее, о чем речь (ниже 2 фрагмента конфигурации - "до" и
"после"):

location /webapp/ {
        proxy_pass         http://app_upstream;
 include my-site/proxy_pass_params.conf;
}

Подумалось, что правильнее отдавать статику силами nginx'а, а не напрягать
и без того кривое приложение:) В итоге, location принял следующий облик:

location /webapp/ {
root /var/www/webapps/nginx-static;
 try_files $uri @application-handle;
}

location @application-handle {
        proxy_pass         http://app_upstream;
 include my-site/proxy_pass_params.conf;
}

В результате, в принципе, получил то, что хотел: запрашиваемые файлы
сначала ищутся в /var/www/webapps/nginx-static, и, если ничего не нашли, -
проксируем на приложение. Но - перестали обрабатываться запросы вида
http://my.site.com/webapp, хотя запросы http://my.site.com/webapp/ (со
слэшем на конце) обрабатываются корректно.
Оно то, в общем-то, и правильно: в документации сказано:

Если location задан префиксной строкой со слэшом в конце и запросы
> обрабатываются при помощи proxy_pass<http://nginx.org/ru/docs/http/ngx_http_proxy_module.html#proxy_pass>
> , fastcgi_pass<http://nginx.org/ru/docs/http/ngx_http_fastcgi_module.html#fastcgi_pass>
> , scgi_pass<http://nginx.org/ru/docs/http/ngx_http_scgi_module.html#scgi_pass>
> , uwsgi_pass<http://nginx.org/ru/docs/http/ngx_http_uwsgi_module.html#uwsgi_pass>
>  или memcached_pass<http://nginx.org/ru/docs/http/ngx_http_memcached_module.html#memcached_pass>,
> а в ответ на запрос с URI равным этой строке, но без завершающего слэша,
> будет возвращено постоянное перенаправление с кодом 301 на URI с
> добавленным в конец слэшом. Если такое поведение нежелательно, можно задать
> точное совпадение URI и location, например:
>
> location /user/ {
>     proxy_pass http://user.example.com;
> }
>
> location = /user {
>     proxy_pass http://login.example.com;
> }
>
>
> Про try_files тут не сказано ни слова.) Но возник законный вопрос: а как
вернуть прежнее поведение? можно ли сделать это "красиво"? или придется
городить магию с реврайтами? а если писать реврайты - куда их пихать.. в
общем, как-то так. Как-то сходу не получилось самому ответить на эти
вопросы, решил поделиться с сообществом. Буду признателен за любые
предложения выхода из ситуации.

Немного информации, которая может быть полезной:

[ root at app1 ~]# nginx -V
> nginx version: nginx/1.0.15
> built by gcc 4.4.6 20110731 (Red Hat 4.4.6-3) (GCC)
> TLS SNI support enabled
> configure arguments: --prefix=/usr/share/nginx --sbin-path=/usr/sbin/nginx
> --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log
> --http-log-path=/var/log/nginx/access.log
> --http-client-body-temp-path=/var/lib/nginx/tmp/client_body
> --http-proxy-temp-path=/var/lib/nginx/tmp/proxy
> --http-fastcgi-temp-path=/var/lib/nginx/tmp/fastcgi
> --http-uwsgi-temp-path=/var/lib/nginx/tmp/uwsgi
> --http-scgi-temp-path=/var/lib/nginx/tmp/scgi --pid-path=/var/run/nginx.pid
> --lock-path=/var/lock/subsys/nginx --user=nginx --group=nginx
> --with-file-aio --with-ipv6 --with-http_ssl_module
> --with-http_realip_module --with-http_addition_module
> --with-http_xslt_module --with-http_image_filter_module
> --with-http_geoip_module --with-http_sub_module --with-http_dav_module
> --with-http_flv_module --with-http_mp4_module
> --with-http_gzip_static_module --with-http_random_index_module
> --with-http_secure_link_module --with-http_degradation_module
> --with-http_stub_status_module --with-http_perl_module --with-mail
> --with-mail_ssl_module --with-cc-opt='-O2 -g -pipe -Wall
> -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector
> --param=ssp-buffer-size=4 -m64 -mtune=generic' --with-ld-opt=-Wl,-E
>
> [ root at app1 ~]# lsb_release -a
> LSB Version:
> :core-4.0-amd64:core-4.0-noarch:graphics-4.0-amd64:graphics-4.0-noarch:printing-4.0-amd64:printing-4.0-noarch
> Distributor ID: CentOS
> Description: CentOS release 6.2 (Final)
> Release: 6.2
> Codename: Final
>
> [ root at app1 ~]# uname -a
> Linux app1 2.6.32-220.23.1.el6.x86_64 #1 SMP Mon Jun 18 18:58:52 BST 2012
> x86_64 x86_64 x86_64 GNU/Linux
>

Содержимое my-site/proxy_pass_params.conf (роли наверняка не играет, но для
полноты картины - надо):

proxy_redirect     off;
>
> proxy_set_header   Host                   $host;
> proxy_set_header   Remote-Addr       $remote_addr;
> proxy_set_header   X-Real-IP            $remote_addr;
> proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
> proxy_set_header   X-Public-Url         http://$host$request_uri;
>
> client_max_body_size                      40m;
> client_body_buffer_size                    128k;
>
> proxy_connect_timeout                    90;
> proxy_send_timeout                        90;
> proxy_read_timeout                         2600;
>
> proxy_buffer_size                           4k;
> proxy_buffers                                 4 32k;
> proxy_busy_buffers_size                 64k;
> proxy_temp_file_write_size              64k;
>

awaiting..
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mailman.nginx.org/pipermail/nginx-ru/attachments/20140109/8a1abf57/attachment-0001.html>


Подробная информация о списке рассылки nginx-ru