I have a web application that wants to access files from a third party site without CORS enabled. The requests can be to an arbitrary domain with arbitrary parameters. I'm sending a request to my domain containing the target encoded as a GET parameter, i.e.
GET https://www.example.com/proxy/?url=http%3A%2F%2Fnginx.org%2Fen%2Fdocs%2Fhttp%2Fngx_http_proxy_module.html
Then in Nginx I do
location /proxy/ {
resolver 8.8.8.8;
set_unescape_uri $dst $arg_url;
proxy_pass $dst;
}
This works for single files, but the target server sometimes returns a Location header, which I want to intercept and modify for the client to retry.
Basically I would like to escape $sent_http_location, append it to https://www.example.com/proxy/?url= and pass that back to the browser to retry.
I've tried doing
set_escape_uri $tmp $sent_http_location;
proxy_redirect $sent_http_header /pass/?v=$tmp;
but this doesn't work. I've also tried saving the Location header, then ignoring the incoming header with
proxy_hide_header
and replacing it with my own
proxy_set_header
but ignoring causes me to lose the variable saving it.
How can I configure Nginx to accomplish this handling of redirects so that I could pass a encoded URL would be returned to the user when the proxied site redirects?
There are several problems with your unsuccessful approach:
proxy_set_header
sets the header that goes to the upstream server, not to the client. So even if $sent_http_location
hadn't been empty, your configuration couldn't possibly work as you wanted it to.
$sent_http_<header>
variables point exactly to the same area of memory as the response headers that will be send to the client. So when proxy_hide_header
takes effect, the specified header is being removed from the memory along with the value of the corresponding $sent_http_<header>
.
set_escape_uri
works at a very early stage of the request processing, way before proxy_pass
is called and the Location
header is returned from the upstream server. So it will always process the at that time empty variable $sent_http_location
and the result also will always be the empty variable.
The last problem is the most serious. The only way to make set_escape_uri
work after proxy_pass
is to force Nginx to leave the current location and start the processing all over again. This can be done with the error_page
trick:
location /proxy/ {
resolver 8.8.8.8;
set_unescape_uri $dst $arg_url;
proxy_pass $dst;
proxy_intercept_errors on;
error_page 301 = @rewrite_301;
}
location @rewrite_301 {
set_escape_uri $location $upstream_http_location;
return 301 /pass/?v=$location;
}
Note the use of $upstream_http_location
instead of $sent_http_location
. When Nginx leaves the context of the location, it assumes that the request will be proxied to another upstream, or processed in some other way, and so it clears the headers recieved from the last proxy_pass
, to make room for new response headers.
Unlike $sent_http_<header>
vairables, which represent response headers that will be send to the client, $upstream_http_<header>
variables represent response headers that were recieved from the upstream. Because of that, they are only replaced with new values when the request is proxied to another upstream server. So, once set, these variables can be used at any moment, they will not be cleared.