Opened 2 weeks ago

Closed 2 weeks ago

Last modified 2 weeks ago

#2304 closed defect (invalid)

error_page not applied vs Transfer-Encoding: chunked

Reported by: ilsixela@… Owned by:
Priority: minor Milestone:
Component: nginx-core Version: 1.19.x
Keywords: error_page Cc: ilsixela@…
uname -a: Linux gateway-79fdb46457-69njm 5.4.0-91-generic #102-Ubuntu SMP Fri Nov 5 16:31:28 UTC 2021 x86_64 Linux
nginx -V: nginx version: nginx/1.21.5
built by gcc 10.3.1 20211027 (Alpine 10.3.1_git20211027)
built with OpenSSL 1.1.1l 24 Aug 2021
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 --with-perl_modules_path=/usr/lib/perl5/vendor_perl --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='-Os -fomit-frame-pointer -g' --with-ld-opt=-Wl,--as-needed,-O1,--sort-common

Description

I've configured an error page in Nginx:

error_page 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 421 422 423 424 425 426 428 429 431 451 495 496 497 500 501 502 503 504 505 506 507 508 510 511 /error.html;
location = /error.html {
  ssi on;
  internal;
  auth_basic off;
  root /etc/nginx;
}

This works fine for a 404. However I'm submitting a request like this:

POST /graphql HTTP/1.1
Content-Length: 2
Transfer-Encoding: chunked
Connection: close

{}

Note Transfer-Encoding: chunked. Nginx returns a 400 Bad Request error page of its own instead of mine. One minor clue is that the footer only says "nginx", it doesn't include a version. I tried adding "server_tokens off;" and it didn't change anything.

How do I make Nginx use my error page for ALL errors, please?

Change History (3)

comment:1 by Maxim Dounin, 2 weeks ago

Resolution: invalid
Status: newclosed

Note Transfer-Encoding: chunked. Nginx returns a 400 Bad Request error page of its own instead of mine.

Work fine here:

$ telnet localhost 8080
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
POST /graphql HTTP/1.1
Content-Length: 2
Transfer-Encoding: chunked
Connection: close

HTTP/1.1 400 Bad Request
Server: nginx/1.21.6
Date: Thu, 13 Jan 2022 13:21:06 GMT
Content-Type: text/html
Transfer-Encoding: chunked
Connection: close

b
error.html

0

Connection closed by foreign host.

Make sure you are configuring error pages in the default server, and not a name-based virtual servers: your request doesn't contain the "Host" header and the error will be generated in the context of the default server.

Note well that it might not be a good idea to configure custom error pages for low-level errors such as 400: these errors happen at early stages of request processing and some of the request properties might not be set, potentially resulting in unexpected behaviour.

One minor clue is that the footer only says "nginx", it doesn't include a version. I tried adding "server_tokens off;" and it didn't change anything.

With "server_tokens off;" default error pages (and the Server response header) are expected to contain "nginx" without a version. With "server_tokens on;" (the default) there will be "nginx/<version>", see docs.

comment:2 by ilsixela@…, 2 weeks ago

Thank you very much for your response! Please could I see the config you used to test with?

This is my full config, I'm no expert but I'm pretty sure I'm applying the error_page to the default server? The presence of a Host header made no difference from Burp but it does change with curl - no host gives me the 400 Bad Request, with host I get 401 Unauthorized. Also tried with telnet, see below.

events {}
http {
  server {
    listen 80;
    auth_request /authorise;
    server_tokens off;

    location = /ping {
      return 200;
    }

    error_page 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 421 422 423 424 425 426 428 429 431 451 495 496 497 500 501 502 503 504 505 506 507 508 510 511 /error.html;
    location = /error.html {
      ssi on;
      internal;
      auth_basic off;
      root /etc/nginx;
    }

    location = /authorise {
      internal;
      proxy_pass_request_body off;
      proxy_set_header Content-Length 0;
      proxy_http_version 1.1;
      proxy_pass --elided to avoid "external link"--
    }

    location /graphql {
      auth_request_set $auth_token $upstream_http_authorization;
      proxy_set_header Authorization $auth_token;
      proxy_http_version 1.1;
      proxy_pass --elided to avoid "external link"--
    }
    location /subscriptions {
      auth_request_set $auth_token $upstream_http_authorization;
      proxy_set_header Authorization $auth_token;
      proxy_set_header Upgrade $http_upgrade;
      proxy_set_header Connection $http_connection;
      proxy_http_version 1.1;
      proxy_pass --elided to avoid "external link"--
    }
  }
}

Telnet output, no host:

$ telnet tempest.minikube 80
Trying 192.168.49.2...
Connected to tempest.minikube.
Escape character is '^]'.
POST /graphql HTTP/1.1
Content-Length: 2
Transfer-Encoding: chunked
Connection: close

HTTP/1.1 400 Bad Request
Date: Fri, 14 Jan 2022 13:43:59 GMT
Content-Type: text/html
Content-Length: 150
Connection: close

<html>
<head><title>400 Bad Request</title></head>
<body>
<center><h1>400 Bad Request</h1></center>
<hr><center>nginx</center>
</body>
</html>
Connection closed by foreign host.

With Host header:

$ telnet tempest.minikube 80
Trying 192.168.49.2...
Connected to tempest.minikube.
Escape character is '^]'.
POST /graphql HTTP/1.1
Host: tempest.minikube
Content-Length: 2
Transfer-Encoding: chunked
Connection: close


HTTP/1.1 400 Bad Request
Date: Fri, 14 Jan 2022 13:47:34 GMT
Content-Type: text/html
Content-Length: 150
Connection: close

<html>
<head><title>400 Bad Request</title></head>
<body>
<center><h1>400 Bad Request</h1></center>
<hr><center>nginx</center>
</body>
</html>
Connection closed by foreign host.

I tried to test with 1.21.6 but I don't think that's available yet, not as an Alpine container at least.

comment:3 by Maxim Dounin, 2 weeks ago

Please could I see the config you used to test with?

I've used the exact configuration you've provided with minimal required additions/modifications:

events {
}

http {
    server {
        listen 8080;

        error_page 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 421 422 423 424 425 426 428 429 431 451 495 496 497 500 501 502 503 504 505 506 507 508 510 511 /error.html;
        location = /error.html {
            ssi on;
            internal;
            auth_basic off;
            root /tmp/;
        }
    }
}

I tried to test with 1.21.6 but I don't think that's available yet, not as an Alpine container at least.

It's a version from the mainline branch, it's basically the same as 1.21.5 and no different from any other nginx version when it comes to the problem you are trying to debug.

First of all you may want to check that you are connecting to nginx directly, without any intermediate proxies, and that the configuration you are editing is the actual configuration being used.

Note: See TracTickets for help on using tickets.