# Rails XSendfile via Nginx

**URL:** <https://www.ruby-forum.com/t/rails-xsendfile-via-nginx/219577>\
**Category:** NGINX\
**Created:** [May 8, 2012, 12:53am UTC](https://www.ruby-forum.com/t/rails-xsendfile-via-nginx/219577 "2012-05-08T00:53:39Z")\
**Posts on this page:** 12\
**Page:** 1

<div class="post-metadata">

**Author:** ![dubstep](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/cdc98d/32.png) [@dubstep](https://www.ruby-forum.com/u/dubstep)\
**Post date:** [May 8, 2012, 12:53am UTC](https://www.ruby-forum.com/t/rails-xsendfile-via-nginx/219577/1 "2012-05-08T00:53:39Z")

</div>

Hi,

I’m trying to configure nginx to serve a video file on behalf of my  
rails 3 backend using xsendfile. I want rails to redirect the request  
for [应用宝官网-全网最新最热手机应用游戏下载](http://myapp.com/recipes/1/video) to nginx to handle. Unfortunately,  
my current configuration results in a 404 (page not found) error. I’ve  
pasted my nginx configuration at [http://pastebin.com/VA4QFM35](http://pastebin.com/VA4QFM35)

Does anyone know what could be wrong?

Note: I have activated X-Accel-Redirect in rails by:

# Rails configuration (e.g. config/environments/production.rb)

config.action\_dispatch.x\_sendfile\_header = ‘X-Accel-Redirect’

Thanks.

Ari

---

<div class="post-metadata">

**Author:** ![folkbaroque](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/f/fbc32d/32.png) [@folkbaroque](https://www.ruby-forum.com/u/folkbaroque)\
**Post date:** [May 8, 2012, 1:17am UTC](https://www.ruby-forum.com/t/rails-xsendfile-via-nginx/219577/2 "2012-05-08T01:17:51Z")

</div>

On 7 May 2012 23:53, Ari K. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> Note: I have activated X-Accel-Redirect in rails by:
> 
> # Rails configuration (e.g. config/environments/production.rb)
> 
> config.action\_dispatch.x\_sendfile\_header = ‘X-Accel-Redirect’

Steps you could take / info you could provide:

- hit the app direct with a curl invocation and examine the headers it  
creates. I bet you’ll find the problem here.
- re-read the alias docs. Your location{/recipe/…} looks a bit  
screwy, if the examples you’ve given are precise.

## J

Jonathan M.  
Oxford, London, UK

> **[Jonathan Matthews :: Words Typed, Great Rates For Cash](https://www.jpluscplusm.com)**

---

<div class="post-metadata">

**Author:** ![folkbaroque](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/f/fbc32d/32.png) [@folkbaroque](https://www.ruby-forum.com/u/folkbaroque)\
**Post date:** [May 8, 2012, 1:20am UTC](https://www.ruby-forum.com/t/rails-xsendfile-via-nginx/219577/3 "2012-05-08T01:20:22Z")

</div>

On 8 May 2012 00:16, Jonathan M. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> > 
> 
> screwy, if the examples you’ve given are precise.  
> I forget step #0: examine the error log! It’ll tell you where nginx is  
> failing to find your file, hence what the config problem might be …

## J

Jonathan M.  
Oxford, London, UK

> **[Jonathan Matthews :: Words Typed, Great Rates For Cash](https://www.jpluscplusm.com)**

---

<div class="post-metadata">

**Author:** ![folkbaroque](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/f/fbc32d/32.png) [@folkbaroque](https://www.ruby-forum.com/u/folkbaroque)\
**Post date:** [May 9, 2012, 12:21pm UTC](https://www.ruby-forum.com/t/rails-xsendfile-via-nginx/219577/4 "2012-05-09T12:21:19Z")

</div>

On 9 May 2012 03:58, Ari K. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> Prior to asking my original question, I checked my error.log and found  
> no errors whatsoever. I also tried using curl, but the content returned  
> is a Nginx 404 error page. Any other ideas? I also doubled checked my  
> alias configuration; as per nginx docs, location & alias take regex  
> expressions and captures. My hunch is that the url is the problem  
> (/recipes/1/video) since it does NOT contain the filename.

Yes, that’s what I meant about your use of “alias” being screwy. You  
haven’t indicated that you’ve tried hitting the app _directly_,  
without nginx in the way, and examining the headers. This is always  
really useful. In this case, however, I think the fault lies  
elsewhere.

## Here’s what you’ve got in your config:

## location /recipes/(.\*)/video { alias /var/www/app/current/uploads/videos/$1/original/; }

To me, this looks like when you request

[http://foo.bar.com/recipes/123/video](http://foo.bar.com/recipes/123/video)

you’re asking nginx to serve the “file”

/var/www/app/current/uploads/videos/123/original/

But this isn’t a file, it’s a directory. So  
[GitHub - nginxinc/nginx-wiki: ARCHIVED -- Source for the now archived NGINX Wiki section of https://www.nginx.com · GitHub](http://wiki.nginx.org/HttpIndexModule#index) kicks in and (unless  
you’ve changed it) tries to serve  
/var/www/app/current/uploads/videos/123/original/index.html.  
Hence the 404.

I think your taxonomy is a bit fucked here, TBH, and you’re reaping  
the rewards of trying to implement the wrong public-facing data  
structure.  
If you have the scope to change it, I’d be going with something where  
(a) “video” is on the left of the video ID and possibly (b) where  
/recipes/ is out of the picture all together. Something like

[http://foo.bar.com/video/123](http://foo.bar.com/video/123) or, if you anticipate getting to any scale  
[http://foo.bar.com/video/000/1/123](http://foo.bar.com/video/000/1/123) or, if “recipe” is still important  
[http://foo.bar.com/video/recipes/123](http://foo.bar.com/video/recipes/123) to allow you to have non-recipe  
based videos in the future.

… and where the _file_ on disk is named according to the ID - not  
where the ID is just part of the filesystem path leading up to the  
video.

## HTH, Jonathan

Jonathan M.  
Oxford, London, UK

> **[Jonathan Matthews :: Words Typed, Great Rates For Cash](https://www.jpluscplusm.com)**

---

<div class="post-metadata">

**Author:** ![folkbaroque](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/f/fbc32d/32.png) [@folkbaroque](https://www.ruby-forum.com/u/folkbaroque)\
**Post date:** [May 9, 2012, 4:58am UTC](https://www.ruby-forum.com/t/rails-xsendfile-via-nginx/219577/5 "2012-05-09T04:58:55Z")

</div>

Jonathan M. wrote in post #1059929:

> On 8 May 2012 00:16, Jonathan M. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:
> 
> > > 
> > 
> > screwy, if the examples you’ve given are precise.  
> > I forget step #0: examine the error log! It’ll tell you where nginx is  
> > failing to find your file, hence what the config problem might be …
> 
> ## J
> 
> Jonathan M.  
> Oxford, London, UK  
> [http://www.jpluscplusm.com/contact.html](http://www.jpluscplusm.com/contact.html)

Prior to asking my original question, I checked my error.log and found  
no errors whatsoever. I also tried using curl, but the content returned  
is a Nginx 404 error page. Any other ideas? I also doubled checked my  
alias configuration; as per nginx docs, location & alias take regex  
expressions and captures. My hunch is that the url is the problem  
(/recipes/1/video) since it does NOT contain the filename.

Thanks.

-Ari

---

<div class="post-metadata">

**Author:** ![folkbaroque](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/f/fbc32d/32.png) [@folkbaroque](https://www.ruby-forum.com/u/folkbaroque)\
**Post date:** [May 9, 2012, 1:25pm UTC](https://www.ruby-forum.com/t/rails-xsendfile-via-nginx/219577/6 "2012-05-09T13:25:21Z")

</div>

On Wed, May 09, 2012 at 04:58:55AM +0200, Ari K. wrote:

> Jonathan M. wrote in post #1059929:

Hi there,

> Prior to asking my original question, I checked my error.log and found  
> no errors whatsoever. I also tried using curl, but the content returned  
> is a Nginx 404 error page. Any other ideas? I also doubled checked my  
> alias configuration; as per nginx docs, location & alias take regex  
> expressions and captures. My hunch is that the url is the problem  
> (/recipes/1/video) since it does NOT contain the filename.

your config has two location{} blocks:

location /  
location /recipes/(.\*)/video

Probably you want to include “~” in your second location directive:

> **[Module ngx\_http\_core\_module](http://nginx.org/en/docs/http/ngx_http_core_module.html#location)**
>
> This directive appeared in version 1.11.8.

That might be sufficient to get everything working for you.

It’s not clear to me exactly how you wish things to work. If the above  
change gets things working, then it doesn’t matter that it’s not clear  
to me. But:

the usual way to use X-Accel-Redirect is that the client accesses  
“/public/uri” which nginx sends to an upstream for processing; that  
upstream returns X-Accel-Redirect to “/private/uri”; and nginx is  
configured to serve “/private/uri” from the filesystem.

So: if the client wishes to get access to the file “test.video”, what  
public url will they use to access it? What is the X-Accel-Redirect  
header that the backend sends to nginx? And where on the filesystem is  
the file “test.video”? If you can answer those questions, then it may  
become clear how nginx should be configured to allow it all to happen.

## f

Francis D. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid)

---

<div class="post-metadata">

**Author:** ![folkbaroque](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/f/fbc32d/32.png) [@folkbaroque](https://www.ruby-forum.com/u/folkbaroque)\
**Post date:** [May 9, 2012, 11:21pm UTC](https://www.ruby-forum.com/t/rails-xsendfile-via-nginx/219577/7 "2012-05-09T23:21:23Z")

</div>

Jonathan/Francis,

I modified the applications url paths to–

[www.foo.bar.com/recipes/:id/video/:filename](http://www.foo.bar.com/recipes/:id/video/:filename)

> Probably you want to include “~” in your second location directive:

Tried adding the (regex) tilde, but that did not solve the issue.

> So: if the client wishes to get access to the file “test.video”, what  
> public url will they use to access it?

[www.foo.bar.com/recipes/1/video/example\_one.ogg](http://www.foo.bar.com/recipes/1/video/example_one.ogg)

> What is the X-Accel-Redirect header that the backend sends to nginx?

How can I find this out? When I curl directly to the backend server I  
get these headers: see [http://pastebin.com/CVLjhLRJ](http://pastebin.com/CVLjhLRJ)

> And where on the filesystem is the file “test.video”?

The video files are stored according to their respective ids; note the  
paths are different AFTER the “videos” directory –

/var/www/app/current/uploads/videos/1/original/example\_one.ogg

/var/www/app/current/uploads/videos/2/original/example\_two.ogg

This leads me to believe the issue is with the alias mapping, i.e.

/var/www/app/current/uploads/videos/=/video;

I tried changing it to the following, but that did not work. I’m not  
even sure if that type of mapping is allowed. Does anyone know if alias  
mapping allows regex? I couldn’t find any thing in the docs.

/lessons/(.\*)/video/=/var/www/app/current/uploads/videos/$1/original/;

Appreciate the help.

-Ari

---

<div class="post-metadata">

**Author:** ![folkbaroque](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/f/fbc32d/32.png) [@folkbaroque](https://www.ruby-forum.com/u/folkbaroque)\
**Post date:** [May 10, 2012, 12:17am UTC](https://www.ruby-forum.com/t/rails-xsendfile-via-nginx/219577/8 "2012-05-10T00:17:14Z")

</div>

> Appreciate the help.
> 
> -Ari

**I also tried hard-coded values, but that still results in a 404  
error.**

upstream xxx.xxx.xxx.xxx {  
server 127.0.0.1:3000;  
server 127.0.0.1:3001;  
server 127.0.0.1:3002;  
}

location ~ /lessons/1/video/(.\*) {  
internal;  
alias /var/www/app/current/uploads/videos/1/original/$1;  
}

location / {  
proxy\_redirect off;

```
         proxy_set_header X-Real-IP $remote_addr;
         proxy_set_header X-Forwarded-For

```

$proxy\_add\_x\_forwarded\_for;

```
         proxy_set_header X-Sendfile-Type X-Accel-Redirect;
         proxy_set_header X-Accel-Mapping

```

/lessons/1/video/=/var/www/app/current/uploads/videos/1/original/;

```
         proxy_connect_timeout 75;
         proxy_send_timeout 60;
         proxy_read_timeout 60;
         root /var/www/app/current/public;

         if (!-f $request_filename) {
             rewrite ^(.*)$ $1 break;
             proxy_pass http://xxx.xxx.xxx.xxx;
             break;
         }
     }

```

---

<div class="post-metadata">

**Author:** ![folkbaroque](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/f/fbc32d/32.png) [@folkbaroque](https://www.ruby-forum.com/u/folkbaroque)\
**Post date:** [May 10, 2012, 12:38am UTC](https://www.ruby-forum.com/t/rails-xsendfile-via-nginx/219577/9 "2012-05-10T00:38:26Z")

</div>

On Wed, May 09, 2012 at 11:21:23PM +0200, Ari K. wrote:

Hi there,

> I modified the applications url paths to–
> 
> [www.foo.bar.com/recipes/:id/video/:filename](http://www.foo.bar.com/recipes/:id/video/:filename)
> 
> > Probably you want to include “~” in your second location directive:
> 
> Tried adding the (regex) tilde, but that did not solve the issue.

Ok, so we’ll have to try to find exactly what you want/expect to happen.

We can then compare it to what currently happens, and see what to change  
to make things match.

> > So: if the client wishes to get access to the file “test.video”, what  
> > public url will they use to access it?
> 
> [www.foo.bar.com/recipes/1/video/example\_one.ogg](http://www.foo.bar.com/recipes/1/video/example_one.ogg)

For this request, the “location” is “/recipes/1/video/example\_one.ogg”.

If your configuration is:

location / {}  
location /recipes/(.\*)/video {}

then that will match the first location.

If your configuration is

location / {}  
location ~ /recipes/(.\*)/video {}

then it will match the second location.

If that request is processed in the first location, I think it will try  
to serve the file

/var/www/app/current/recipes/1/video/example\_one.ogg

if it exists, or else proxy\_pass to

[应用宝官网-全网最新最热手机应用游戏下载](http://www.myapp.com/recipes/1/video/example_one.ogg)

If the request is processed in the second location, it will return 404  
because that location is marked “internal”.

> > What is the X-Accel-Redirect header that the backend sends to nginx?
> 
> How can I find this out? When I curl directly to the backend server I  
> get these headers: see [http://pastebin.com/CVLjhLRJ](http://pastebin.com/CVLjhLRJ)

I suspect that your backend (rails?) server pays attention to the  
various  
headers you have configured using “proxy\_set\_header”, and will respond  
differently if they are present. If you repeat the curl command with  
some or all of those, you might see a different response.

I’d probably look in the debug log to find what nginx thinks is going  
on;  
either that or use something like tcpdump to see what is happening.

> > And where on the filesystem is the file “test.video”?
> 
> The video files are stored according to their respective ids; note the  
> paths are different AFTER the “videos” directory –
> 
> /var/www/app/current/uploads/videos/1/original/example\_one.ogg
> 
> /var/www/app/current/uploads/videos/2/original/example\_two.ogg

Ok, so it’s a file path which can be derived from components of the url.

> This leads me to believe the issue is with the alias mapping, i.e.
> 
> /var/www/app/current/uploads/videos/=/video;

What’s that?

I see it in your

```
  proxy_set_header X-Accel-Mapping 

```

/var/www/app/current/uploads/videos/=/video;

directive, but that’s nothing to do with nginx (unless I’m missing  
something).

> I tried changing it to the following, but that did not work. I’m not  
> even sure if that type of mapping is allowed. Does anyone know if alias  
> mapping allows regex? I couldn’t find any thing in the docs.

I’m not aware of alias mapping.

The docs for the nginx alias directive are at [Module ngx\_http\_core\_module](http://nginx.org/r/alias)

You can use regex captures in the alias directive.

> /lessons/(.\*)/video/=/var/www/app/current/uploads/videos/$1/original/;

I’m still unclear on what it is that you want.

When someone requests the url

[www.foo.bar.com/recipes/1/video/example\_one.ogg](http://www.foo.bar.com/recipes/1/video/example_one.ogg)

do you want nginx to directly send them the contents of the file

/var/www/app/current/uploads/videos/1/original/example\_one.ogg

or do you want your rails backend server to do some processing and  
decide whether or not they should be sent the contents of that file,  
or do you want something else to happen? (If “something else”, then  
describe what else.)

If we can start with a clear understanding of the answer to that  
question,  
then the rest of the configuration may become obvious.

## f

Francis D. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid)

---

<div class="post-metadata">

**Author:** ![folkbaroque](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/f/fbc32d/32.png) [@folkbaroque](https://www.ruby-forum.com/u/folkbaroque)\
**Post date:** [May 10, 2012, 12:41am UTC](https://www.ruby-forum.com/t/rails-xsendfile-via-nginx/219577/10 "2012-05-10T00:41:31Z")

</div>

On Thu, May 10, 2012 at 12:17:14AM +0200, Ari K. wrote:

> **I also tried hard-coded values, but that still results in a 404  
> error.**

> location ~ /lessons/1/video/(.\*) {  
> internal;

“internal” means “return 404 if this is requested directly”.

> **[Module ngx\_http\_core\_module](http://nginx.org/en/docs/http/ngx_http_core_module.html#internal)**
>
> This directive appeared in version 1.11.8.

> ```
> alias /var/www/app/current/uploads/videos/1/original/$1;
> }
> 
> ```

## f

Francis D. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid)

---

<div class="post-metadata">

**Author:** ![folkbaroque](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/f/fbc32d/32.png) [@folkbaroque](https://www.ruby-forum.com/u/folkbaroque)\
**Post date:** [May 10, 2012, 10:46pm UTC](https://www.ruby-forum.com/t/rails-xsendfile-via-nginx/219577/11 "2012-05-10T22:46:41Z")

</div>

> or do you want your rails backend server to do some processing and  
> decide whether or not they should be sent the contents of that file,  
> or do you want something else to happen? (If “something else”, then  
> describe what else.)

I want rails to authenticate/authorize access to the content in question  
and then have nginx serve that file. The following articles  
explain/demonstrate what I’m trying to do:

[http://rack.rubyforge.org/doc/Rack/Sendfile.html](http://rack.rubyforge.org/doc/Rack/Sendfile.html)

[http://thedataasylum.com/articles/how-rails-nginx-x-accel-redirect-work-together.html](http://thedataasylum.com/articles/how-rails-nginx-x-accel-redirect-work-together.html)

Thanks.

-Ari

---

<div class="post-metadata">

**Author:** ![folkbaroque](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/f/fbc32d/32.png) [@folkbaroque](https://www.ruby-forum.com/u/folkbaroque)\
**Post date:** [May 11, 2012, 2:37am UTC](https://www.ruby-forum.com/t/rails-xsendfile-via-nginx/219577/12 "2012-05-11T02:37:17Z")

</div>

On Thu, May 10, 2012 at 10:46:41PM +0200, Ari K. wrote:

> > or do you want your rails backend server to do some processing and  
> > decide whether or not they should be sent the contents of that file,

> I want rails to authenticate/authorize access to the content in question  
> and then have nginx serve that file. The following articles  
> explain/demonstrate what I’m trying to do:
> 
> [http://rack.rubyforge.org/doc/Rack/Sendfile.html](http://rack.rubyforge.org/doc/Rack/Sendfile.html)

[http://thedataasylum.com/articles/how-rails-nginx-x-accel-redirect-work-together.html](http://thedataasylum.com/articles/how-rails-nginx-x-accel-redirect-work-together.html)

That seems pretty clear, and seems to match what was mentioned earlier  
in the thread.

You must configure the nginx location that corresponds to “/public/url”,  
to send the request to rails.

You must configure the rails side to send back a response with  
“X-Accel-Redirect: /private/url”.

You must configure the nginx location that corresponds to “/private/url”  
to serve the correct file.

It is probably simplest (and matches the examples on those two pages  
most closely) if the /private/url that rails returns maps directly to  
a file on the filesystem.

On the nginx side, you want two location{}s – one which matches  
/public/url and one which matches /private/url.

On the rails side, do whatever it takes to get it to turn /public/url  
into /private/url. That will probably involve the alias mapping thing  
you mentioned earlier – but that’s a rails thing, so you’ll get better  
help on a rails list for that.

Your [thedataasylum.com](http://thedataasylum.com) link does seem to give a strong hint what it is  
about, though – in rails code you call send\_file() with the actual  
filename you wish to have sent, and then the X-Accel-Mapping value  
replaces the first bit of that filename with the private url prefix.

It appears that what you have right now is the location that matches  
/public/url not sending the request to rails. And you don’t appear to  
have any specific location matching the private url, which seems to be  
of the form “/video/1/original/example\_one.ogg”.

So, probably, get rid of your “location /recipes/” thing, and add a  
“location /video/” with a suitable alias or root directive.

If you’re still having difficulties, I suggest you show your nginx  
config,  
and show the curl request and response when you make the request of the  
rails server directly, including “-i” for the headers, and five “-H”  
arguments, one for each “proxy\_set\_header” value that you have in your  
nginx config.

(And if that doesn’t make it clear where the problem is, probably also  
show the line in your rails code which does “send\_file()”. But that bit  
will probably be more interesting to a rails list.)

Good luck with it,

## f

Francis D. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid)
