Opened 6 years ago

Closed 6 years ago

#1707 closed defect (duplicate)

try_files doesn't work sometimes

Reported by: fuweichin@… Owned by:
Priority: minor Milestone: nginx-1.15
Component: nginx-core Version: 1.15.x
Keywords: try_files Cc: fuweichin@…
uname -a: Linux qin-workstation 4.4.0-17134-Microsoft #345-Microsoft Wed Sep 19 17:47:00 PST 2018 x86_64 GNU/Linux
nginx -V: nginx version: nginx/1.15.7
built by gcc 6.3.0 20170516 (Debian 6.3.0-18+deb9u1)
built with OpenSSL 1.1.0f 25 May 2017 (running with OpenSSL 1.1.0j 20 Nov 2018)
TLS SNI support enabled
configure arguments: --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp --user=nginx --group=nginx --with-compat --with-file-aio --with-threads --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-mail --with-mail_ssl_module --with-stream --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module --with-cc-opt='-g -O2 -fdebug-prefix-map=/data/builder/debuild/nginx-1.15.7/debian/debuild-base/nginx-1.15.7=. -specs=/usr/share/dpkg/no-pie-compile.specs -fstack-protector-strong -Wformat -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -fPIC' --with-ld-opt='-specs=/usr/share/dpkg/no-pie-link.specs -Wl,-z,relro -Wl,-z,now -Wl,--as-needed -pie'

Description

My site uses HTML5 History state and now I need to configure nginx to support it. Nginx supports HTML5 History state with directive 'try_files' but there is a downside: 'try_files' doesn't try files according to acceptable types of a request, which may cause developers hard to find broken non-html links, e.g. a browser wants an image/css while the server may return a html. To overcome the downside, I'm trying to do some tricks.

site configuration

server {
        listen 80;
        listen 443 ssl;
        server_name qin-workstation.bldgos.net;

        ssl_certificate /etc/ssl/certs/bldgos.net.cer;
        ssl_certificate_key /etc/ssl/private/bldgos.net.key;

        location / {
                root /var/www/html;
                index index.html;
                set $fallback_file $uri/;
                if ($http_accept ~ text/html) {
                        set $fallback_file /index.html;
                }
                add_header Try-Files "$uri $fallback_file" always;   # just to debug
                try_files $uri $fallback_file;
        }
}

server root (ls /var/www/html)

index.html

request headers

GET /new-state HTTP/1.1
Host: qin-workstation.bldgos.net
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7

response headers

HTTP/1.1 404 Not Found
Server: nginx/1.15.7
Date: Thu, 10 Jan 2019 09:37:38 GMT
Content-Type: text/html
Content-Length: 555
Connection: keep-alive
Try-Files: /new-state /index.html

the problem
Now that try_files: $uri $fallback_file is equivalent try_files: /new-state /index.html for the request, why nginx returns 404 and not to try file '/index.html'?

my workaround to the problem

                set $fallback_file /index.html;
                if ($uri ~ "\.(js|css|png|jpg|gif|ico)$") {
                       set $fallback_file $uri/;
                }
                try_files $uri $fallback_file;

conclusion
Since 'try_files' can only be put into scope 'server' and 'location', but not 'if', I guess that the processing of 'try_files' may be prior to 'if'.

Change History (1)

comment:1 by Maxim Dounin, 6 years ago

Resolution: duplicate
Status: newclosed

The try_files directive is not inherited into the if block, and hence not used when the if matches, see ticket #86, in particular this comment.

Also note that using

set $fallback_file $uri/;
try_files $uri $fallback_file;

won't work, as trailing "/" as a flag to test directory existence is only recognized directly in the try_files arguments, not when it comes via a variable.

Note: See TracTickets for help on using tickets.