21

I have an nginx-based HTTP proxy and I would like to process all HTTP redirects inside it so that clients get only the last response in the redirect chain.

The basic code looks like:

location /proxy {
    rewrite ^/proxy/([^/]+) $1 break;

    proxy_pass http://$uri/;
}

My attempt at following 1 level redirects is this:

error_page 301 302 307 =200 @redir;

... and having this named location:

location @redir {
    proxy_pass $proxy_location;
}

Only there's no $proxy_location variable and I failed to find a way of creating it. It should contain the value of Location: header received from upstream.

Any thoughts?

3 Answers 3

9

I believe you want the variable $upstream_http_location.

Variables that begin with $proxy* control what goes from nginx to the upstream. The $upstream* series of variables contains information about the response that nginx itself receives. You can get any aribitrary HTTP header received from an upstream server with $upstream_http_headername.

Note that these $upstream variables cannot be anything but null until the response is received from the upstream server, so there are some limitations on their use.

1
  • 4
    by the rules of stack overflow you should not provide links, which may change in the future. instead you should provide precise working example. I don't know why this incomplete answer was selected but it is not helpful by the stackoveflow standard. vlad's answer is much more helpful.
    – vasilevich
    Commented Oct 23, 2020 at 0:41
41

Here is the full example of what worked for me:

server {
    ...

    location / {
        proxy_pass http://backend;
        # You may need to uncomment the following line if your redirects are relative, e.g. /foo/bar
        #proxy_redirect / /;
        proxy_intercept_errors on;
        error_page 301 302 307 = @handle_redirect;
    }

    location @handle_redirect {
        set $saved_redirect_location '$upstream_http_location';
        proxy_pass $saved_redirect_location;
    }
}
7
  • 2
    Why did you have to save the value of $upstream_http_location in a new variable?
    – Parth Shah
    Commented Nov 10, 2018 at 1:04
  • 1
    I don't know the exact reason, but it doesn't work otherwise... Commented Nov 10, 2018 at 7:09
  • This is great but only catches a single redirect. Any way to handle multiple redirects? Commented Oct 18, 2019 at 13:17
  • 1
    @ThiefMaster I believe this answer is what you're looking for.
    – iBug
    Commented Oct 24, 2019 at 17:11
  • I'm getting the following error 2020/01/23 09:17:46 [error] 1394#0: *1 invalid URL prefix in "", client: xx.xx.28.3, server: www.example.com, request: "GET /test HTTP/1.1", host: "www.example.com" using the @handle_redirect block, any idea ? Commented Jan 23, 2020 at 9:18
1

I had a similar issue while trying to proxy the images from https://picsum.photos website. They return a 302 redirect to a HMAC-signed URL when requesting a particular picture.

My solution is based on the Vlad's answer, however I added some extras (marked as [1] and [2]) to make it working for me:

server {
    listen 80;
    server_name dog.localhost.direct;
    location / { 
        # [1] pass the server name
        proxy_ssl_server_name on;
        proxy_intercept_errors on;
        error_page 301 302 307 = @handle_redirect;
        proxy_pass https://picsum.photos/id/237/536/354;
    }
    location @handle_redirect {
        set $saved_redirect_location '$upstream_http_location';
        # [2] specify a resolver
        resolver 8.8.8.8;
        proxy_pass $saved_redirect_location;
    }
}

Hope this will help somebody.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .