6

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?

anonymous-one
  • 958
  • 4
  • 26
  • 43

3 Answers3

10

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).

Michael Hampton
  • 237,123
  • 42
  • 477
  • 940
  • 1
    Beautiful. Thank you. We strive to maintain as close to RFC compliance as possible. We will alter the bodies to include a text link with the target url, as these bodies currently do not include them. That we know how to do. Thanks! – anonymous-one Sep 03 '12 at 06:55
  • 2
    There's little reason not to do https://serverfault.com/a/870911/110020. In fact, nginx is all about optimisation, so, it appears that the 301/302 bodies never actually contains any links, since it'd be inefficient to simply carry around these links to compose documents that noone ever looks at. – cnst Aug 28 '17 at 22:22
8

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 the URL.

  • Specify default_type of "", which appears to remove the Content-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?! ^_^

cnst
  • 12,948
  • 7
  • 51
  • 75
  • I'd run a benchmark against this no matter how pointless it is. Guess benchmark may show that it is better to keep the stock response. – sanmai Nov 18 '17 at 01:45
  • @sanmai, it depends on what you're optimising for; the difference might be quite substancial for traffic counting, for example; it might also be the tipping point between needing to send an extra packet for the body or not. – cnst Nov 19 '17 at 03:41
0

It's a bit ugly, but perhaps you could proxy 301/302 requests and use the proxy_method to change GET to HEAD requests. I haven't tested it, but head requests should only send headers without responses or (hopefully) content-* headers.

http://wiki.nginx.org/NginxHttpProxyModule#proxy_method

blueben
  • 3,487
  • 1
  • 15
  • 15