NGINX 301 and 302 serving small nginx document body. Any way to remove this behaviour?
We have noticed that when using nginx internal 301 and 302 handling, nginx will serve a small document body with the appropriate Location: ... header.
Something along the lines of (in html): 301 redirect - nginx.
As appropriate in the above behaviour, a content-type text/html and content-length header is also sent.
We do a lot of 302 and some 301 redirects, the above behaviour is wasted bandwidth in our opinion.
Any way to disable this behaviour?
One idea that crossed our mind was to set error_page 301 302 to an empty text file. We have not tested this yet, but I am assuming even with the above, the content-type and content-length (0) headers will be sent.
So, is there a clean way to send a "body-less" 301/302 redirect with nginx?
Solution 1:
Think very carefully about what you're asking for, and strongly consider not doing it.
RFC 2616 specifies that the entity bodies you want to remove should be present.
10.3.2 301 Moved Permanently
The new permanent URI SHOULD be given by the Location field in the response. Unless the request method was HEAD, the entity of the response SHOULD contain a short hypertext note with a hyperlink to the new URI(s).
and...
10.3.3 302 Found
The temporary URI SHOULD be given by the Location field in the response. Unless the request method was HEAD, the entity of the response SHOULD contain a short hypertext note with a hyperlink to the new URI(s).
SHOULD, in this context, is defined in RFC 2119:
This word, or the adjective "RECOMMENDED", mean that there may exist valid reasons in particular circumstances to ignore a particular item, but the full implications must be understood and carefully weighed before choosing a different course.
Now you can do this without violating the RFC, but you should be aware of the full implications:
- You are doing a lot of work for virtually no benefit. The only logical reason I can think of to disable the entity body is to save on bandwidth costs, and indeed this is the reason you mentioned, but the difference is so minimal that it's unlikely you'll even see a difference on your bandwidth graphs.
- A very tiny fraction of web clients don't automatically follow 3xx redirects. This fraction was much larger when the RFC was written, which is why this is there in the first place, but there are still ancient monstrosities lurking in the shadows of dark bedrooms and data center closets, and sometimes they come out to play. The one you are most likely to see is
curl
, which is still in common use.
This recommendation has been relaxed somewhat with RFC 7231, which merely says (for both 301 and 302):
The server's response payload usually contains a short hypertext note with a hyperlink to the new URI(s).
The server's response payload usually contains a short hypertext note with a hyperlink to the different URI(s).
Solution 2:
Yes, you can ABSOLUTELY do it with NGINX!
Simply install an exception handler, a.k.a.
error_page
, to post-process required responses. Make sure to set it in such a way as to prevent the error page from modifying the HTTP Status Code, e.g., don't use the=
parameter (or use it to hardcode whichever code you desire).Make sure to
return
a response with a return status code that allows you to optionally set the[text]
, not theURL
.Specify
default_type
of""
, which appears to remove theContent-Type
header
Here's the full code, also at my GitHub in StackOverflow.cnst.nginx.conf
repository:
# cat sf.421976.301-302-redirect-w-no-http-body-text.nginx.conf | sed 's#^#\t#g'
server {
listen 1976;
error_page 301 302 @30x; # keep original HTTP status code w/o `=`
location @30x {
default_type ""; # will remove Content-Type completely
# `300` is a filler: client will get the original HTTP status code
return 300;
}
return 301 http://example.su/test;
}
Here's the confirmation of it working properly:
% curl -i localhost:1976 | sed 's#^#\t#g'
HTTP/1.1 301 Moved Permanently
Server: nginx/1.2.1
Date: Mon, 28 Aug 2017 22:02:41 GMT
Content-Length: 0
Connection: keep-alive
Location: http://example.su/test
%
I've tried it in the browsers, and it worked fine there, too.
P.S. Another option would be to modify the source code, and edit the ngx_http_error_301_page
et al variables, but why go the hard route?! ^_^