accounted4
accounted4

Reputation: 1705

Nginx proxy redirect to another URI

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

Answers (1)

accounted4
accounted4

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

Related Questions