Opened 3 years ago

Closed 3 years ago

#1341 closed defect (wontfix)

Inappropriately includes caching headers when error_page used with https://$host

Reported by: lope@… Owned by:
Priority: critical Milestone:
Component: nginx-core Version: 1.13.x
Keywords: error_page expires Cc:
uname -a: Linux webserver 2.6.32-openvz-042stab113.11-amd64 #1 SMP Fri Dec 18 17:40:41 MSK 2015 x86_64 GNU/Linux
nginx -V: 1.13.3

Description

Firstly it's quite silly that error_page 502 /foo uses port 80 when the connection is https on port 443.

Secondly if using something like this:

proxy_pass $webappaddr;
error_page 502 https://$host/maintenance/maintenance.html;
expires 10y;

Nginx sends the browser a 302 Moved Temporarily header telling it to go to the https URL, and tells the browser to cache the 302 response for 10 years.

The problem is that this is an error state.
It's sending the browser 302 because the webapp is down.
The browser doesn't know that 302 is an error, and so it respects the instruction to cache the "Temporary" redirect, temporarily for 10 years. So no user will ever see the webapp for the next 10 years.

Nginx should not be including expires >0 when responding to error_page anything.
Instead it would be far more appropriate if we can specify:
no_error { expires 10y; }
error {

expires 0;
add_header Cache-Control 'no-cache, no-store, must-revalidate';

}
Or NginX can do it automatically. Whatever the case may be, this is totally messed up as is.

Change History (3)

comment:1 by MTecknology@…, 3 years ago

As discussed on IRC, this is expected and documented behavior. Using error_page 000 http:// means you will return a redirect (302). The expires documentation tells you that the header will be added for a 302 response. If you want the expires header to be dropped for the error, I would suggest you either organize your configuration file so that it's not included when you don't want it to be or just don't tell error_page to respond with a redirect.

Side note- I would encourage you to avoid blindly slapping a 10y expires on every response it's added for. It's very unlikely your application deals with only timestamped/hashed URLs so it's very likely users will see content that you don't expect them to.

comment:2 by lope@…, 3 years ago

People seemed to have trouble understanding this bug report/complaint.

I'm not saying that NginX is not behaving perfectly according to the documentation.

Yes the config can be hacked to work around this problem.

However I assert that it's not appropriate to serve the same expires headers when the gateway is up as when it is down. This behavior makes no sense.
It makes no sense that when there is an error and there is an internal redirect, it removes the expires headers but an external redirect it adds them.

I'm not going to argue further about this. Do as you wish. I'm just saying NginX can be improved and I've been met with a lot of resistance about maintaining the current undesirable behavior.

in reply to:  description comment:3 by Maxim Dounin, 3 years ago

Resolution: wontfix
Status: newclosed

Replying to lope@…:

Firstly it's quite silly that error_page 502 /foo uses port 80 when the connection is https on port 443.

First of all, error_page 502 /foo uses an internal redirect, and doesn't use ports at all. If you see that it "uses port 80", this means that you are doing something wrong. And you should start investigating this instead of trying to work it around by returning redirects instead of real errors.

Secondly if using something like this:

proxy_pass $webappaddr;
error_page 502 https://$host/maintenance/maintenance.html;
expires 10y;

Nginx sends the browser a 302 Moved Temporarily header telling it to go to the https URL, and tells the browser to cache the 302 response for 10 years.

An obvious solution would be to avoid using redirects instead of returning errors, especially if you are using expires 10y in your configuration.

In general nginx tries hard to prevent people from shooting themselves in the foot, and this is exactly the reason why expires only works for the non-error status codes: to avoid adding expires to various errors. Unfortunately, in this particular case "only non-error status codes" rule doesn't save you from expires, because error_page ... http://... already changed the response code to 302.

While it should be possible to introduce some additional mechanism to prevent expires from working in this particular case as well, it is very likely to be non-intuitive and to have false positives. Also, it certainly won't help when using more than one proxy layer.

So as of now, most realistic recommendation looks as follows:

  1. Avoid using error_page ... http://... unless you understand what you are doing, and why you are using it.
  2. If you are using it nevertheless, keep in mind various caching issues - which may happen on any processing layer, because the error status is lost when an error is changed to a redirect.

In the particular case described the problem is obviously due to error_page ... http://... being used when a normal error_page should be used instead. Switching to a normal error_page will resolve the issue.

Note: See TracTickets for help on using tickets.