Reputation: 1705
Our site is an image repository of sorts. Each image has the notion of an external URL and an internal URL. External URL's are seen by clients and they change as we experiment with SEO. The internal URL's are permanent URL's that point to our image hosting service. We use our Ruby on Rails app to provide the URL translation. Here's an example of a request:
-------- ----- ------- ------- ------------
| | --eURL--> | | --> | | --> | | -iURL--> | |
|client| |CDN| |Nginx| | RoR | |Image Host|
| | <-------- | | <-- | | <-- | | <-IMG--- | |
-------- ----- ------- ------- ------------
The architecture is working, but streaming the image through RoR is inefficient. I want to have Nginx do the proxying. That's what it's for. The proposed architecture would look something like this:
-------- ----- ------- -------
| | --eURL--> | | --> | | ------> | RoR |
|client| |CDN| |Nginx| <-????- | |
| | <-------- | | <-- | | -------
-------- ----- | | ------------
| | -iURL-> |Image Host|
| | <-IMG-- | |
------- ------------
What response can I send to Nginx to have it proxy the data? I don't mind adding Nginx modules to my infrastructure and of course I'm open to changing my nginx.conf.
X-Sendfile is the closest thing I've found, but that only allows streaming from the local filesystem. Maybe there is some other obscure HTTP response header or status code I'm unaware of.
Upvotes: 4
Views: 1621
Reputation: 1705
Use the X-Accel-Redirect
header in combination with a special Nginx location
to have Nginx proxy the remote file.
Here is the location
to add to your Nginx configuration:
# Proxy download
location ~* ^/internal_redirect/(.*?)/(.*) {
# Do not allow people to mess with this location directly
# Only internal redirects are allowed
internal;
# Location-specific logging
access_log logs/internal_redirect.access.log main;
error_log logs/internal_redirect.error.log warn;
# Extract download url from the request
set $download_uri $2;
set $download_host $1;
# Compose download url
set $download_url http://$download_host/$download_uri;
# Set download request headers
proxy_set_header Host $download_host;
proxy_set_header Authorization '';
# The next two lines could be used if your storage
# backend does not support Content-Disposition
# headers used to specify file name browsers use
# when save content to the disk
proxy_hide_header Content-Disposition;
add_header Content-Disposition 'attachment; filename="$args"';
# Do not touch local disks when proxying
# content to clients
proxy_max_temp_file_size 0;
# Download the file and send it to client
proxy_pass $download_url;
}
Now you just have to set the X-Accel-Redirect
header in your responses to Nginx:
# This header will ask nginx to download a file
# from http://some.site.com/secret/url.ext and send it to user
X-Accel-Redirect: /internal_redirect/some.site.com/secret/url.ext
# This header will ask nginx to download a file
# from http://blah.com/secret/url and send it to user as cool.pdf
X-Accel-Redirect: /internal_redirect/blah.com/secret/url?cool.pdf
The full solution was found here. I suggest reading it before implementing.
Upvotes: 4