Opened 5 months ago

Closed 5 months ago

#2103 closed defect (invalid)

First declared upstream site returns 403 forbidden when used with proxy_pass

Reported by: dant89@… Owned by:
Priority: major Milestone:
Component: nginx-core Version: 1.14.x
Keywords: Cc:
uname -a: Linux censored.net 4.15.0-124-generic #127-Ubuntu SMP Fri Nov 6 10:54:43 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
nginx -V: nginx version: nginx/1.14.0 (Ubuntu)
built with OpenSSL 1.1.1 11 Sep 2018
TLS SNI support enabled
configure arguments: --with-cc-opt='-g -O2 -fdebug-prefix-map=/build/nginx-GkiujU/nginx-1.14.0=. -fstack-protector-strong -Wformat -Werror=format-security -fPIC -Wdate-time -D_FORTIFY_SOURCE=2' --with-ld-opt='-Wl,-Bsymbolic-functions -Wl,-z,relro -Wl,-z,now -fPIC' --prefix=/usr/share/nginx --conf-path=/etc/nginx/nginx.conf --http-log-path=/var/log/nginx/access.log --error-log-path=/var/log/nginx/error.log --lock-path=/var/lock/nginx.lock --pid-path=/run/nginx.pid --modules-path=/usr/lib/nginx/modules --http-client-body-temp-path=/var/lib/nginx/body --http-fastcgi-temp-path=/var/lib/nginx/fastcgi --http-proxy-temp-path=/var/lib/nginx/proxy --http-scgi-temp-path=/var/lib/nginx/scgi --http-uwsgi-temp-path=/var/lib/nginx/uwsgi --with-debug --with-pcre-jit --with-http_ssl_module --with-http_stub_status_module --with-http_realip_module --with-http_auth_request_module --with-http_v2_module --with-http_dav_module --with-http_slice_module --with-threads --with-http_addition_module --with-http_flv_module --with-http_geoip_module=dynamic --with-http_gunzip_module --with-http_gzip_static_module --with-http_image_filter_module=dynamic --with-http_mp4_module --with-http_perl_module=dynamic --with-http_random_index_module --with-http_secure_link_module --with-http_sub_module --with-http_xslt_module=dynamic --with-mail=dynamic --with-mail_ssl_module --with-stream=dynamic --with-stream_ssl_module --with-stream_ssl_preread_module --add-dynamic-module=/build/nginx-GkiujU/nginx-1.14.0/debian/modules/http-headers-more-filter --add-dynamic-module=/build/nginx-GkiujU/nginx-1.14.0/debian/modules/http-auth-pam --add-dynamic-module=/build/nginx-GkiujU/nginx-1.14.0/debian/modules/http-cache-purge --add-dynamic-module=/build/nginx-GkiujU/nginx-1.14.0/debian/modules/http-dav-ext --add-dynamic-module=/build/nginx-GkiujU/nginx-1.14.0/debian/modules/http-ndk --add-dynamic-module=/build/nginx-GkiujU/nginx-1.14.0/debian/modules/http-echo --add-dynamic-module=/build/nginx-GkiujU/nginx-1.14.0/debian/modules/http-fancyindex --add-dynamic-module=/build/nginx-GkiujU/nginx-1.14.0/debian/modules/nchan --add-dynamic-module=/build/nginx-GkiujU/nginx-1.14.0/debian/modules/http-lua --add-dynamic-module=/build/nginx-GkiujU/nginx-1.14.0/debian/modules/rtmp --add-dynamic-module=/build/nginx-GkiujU/nginx-1.14.0/debian/modules/http-uploadprogress --add-dynamic-module=/build/nginx-GkiujU/nginx-1.14.0/debian/modules/http-upstream-fair --add-dynamic-module=/build/nginx-GkiujU/nginx-1.14.0/debian/modules/http-subs-filter

Description (last modified by dant89@…)

I have a versioned API with two separate repository file paths, I'm trying to use location matching combined with proxy_path to route to the applicable upstream server.

All of the paths work apart from the root path for the first upstream server, for example:

Works:

  • api.com/api/v1/endpoint - 200 response
  • api.com/api/v2 - 200 response
  • api.com/api/v2/endpoint - 200 response

Does not work:

  • api.com/api/v1 - 403 Forbidden

For some reason the non working URL returns a 301, redirects to the same url with a slash on the end and then looks in the wrong location for the file. This seems strange because other URLs such as api/v1/endpoint do not do this behaviour and serve from the correct server folder.

access log

127.0.0.1 - - [02/Dec/2020:16:19:09 +0000] "GET /api/v1 HTTP/1.0" 301 194 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36"
127.0.0.1 - - [02/Dec/2020:16:19:09 +0000] "GET /api/v1/ HTTP/1.0" 403 580 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36"

error log

2020/12/02 16:19:09 [error] 3286#3286: *7 directory index of "/srv/api-v1/public/api/v1/" is forbidden, client: 127.0.0.1, server: api, request: "GET /api/v1/ HTTP/1.0", host: "api"

This is with the following configuration:

upstream v1 {
    server 127.0.0.1;
}

upstream v2 {
    server 127.0.0.1;
}

server {
    listen 443 http2;
    listen [::]:443 http2;
    server_name api.com;
 
    location /api/v1 {
        proxy_pass http://v1;
    }

    location /api/v2 {
        proxy_pass http://v2;
    }
}

server {
    server_name v1;
    root /srv/api-v1/public/;

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

    location ~ ^/index\.php(/|$) {
        include /etc/nginx/fastcgi.conf;
        fastcgi_pass unix:/run/php-fpm-php7.2.socket;
        fastcgi_split_path_info ^(.+?\.php)(/.*)$;
        internal;
    }
}

server {
    server_name v2;
    root /srv/api-v2/public/;

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

    location ~ ^/index\.php(/|$) {
        include /etc/nginx/fastcgi.conf;
        fastcgi_pass unix:/run/php-fpm-php7.2.socket;
        fastcgi_split_path_info ^(.+?\.php)(/.*)$;
        internal;
    }
}

I have documented the issue here in the *Attempted fix* section: https://stackoverflow.com/questions/65093486/nginx-alias-breaks-due-to-try-files-uri-alias-bug

Change History (2)

comment:1 by dant89@…, 5 months ago

Description: modified (diff)

comment:2 by Maxim Dounin, 5 months ago

Resolution: invalid
Status: newclosed

For some reason the non working URL returns a 301, redirects to the same url with a slash on the end and then looks in the wrong location for the file. This seems strange because other URLs such as api/v1/endpoint do not do this behaviour and serve from the correct server folder.

Your configuration contains try_files $uri $uri/ /index.php$is_args$args;. As long as directory /srv/api-v1/public/api/v1/ exists, the $uri/ test will succeed, so requests to /api/v1 and /api/v2 will be handled in the location /, that is, as requests to a directory. And this is exactly what happens as per your logs: the request without a trailing slash is responded with a 301 redirect with the trailing slash added, and the request with the trailing slash is rejected, because there are no index files and autoindex is not enabled.

Note well that instead of trying to invent sophisticated proxy and try_files-based routing, a better solution might be to directly pass appropriate locations to PHP with SCRIPT_FILENAME explicitly set, for example:

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name api.com;
 
    location /api/v1 {
        fastcgi_pass unix:/run/php-fpm-php7.2.socket;
        fastcgi_param SCRIPT_FILENAME /srv/api-v1/public/index.php;
        fastcgi_param PATH_INFO $uri;
        include fastcgi_params;
    }

    location /api/v2 {
        fastcgi_pass unix:/run/php-fpm-php7.2.socket;
        fastcgi_param SCRIPT_FILENAME /srv/api-v2/public/index.php;
        fastcgi_param PATH_INFO $uri;
        include fastcgi_params;
    }
}

If you have further questions on how to configure nginx, consider using support options available.

Note: See TracTickets for help on using tickets.