# Secret URLs and file downloads

**URL:** <https://www.ruby-forum.com/t/secret-urls-and-file-downloads/84154>\
**Category:** Rails\
**Created:** [March 20, 2006, 3:40pm UTC](https://www.ruby-forum.com/t/secret-urls-and-file-downloads/84154 "2006-03-20T15:40:52Z")\
**Posts on this page:** 17\
**Page:** 1

<div class="post-metadata">

**Author:** ![david](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/aeb1de/32.png) [@david](https://www.ruby-forum.com/u/david)\
**Post date:** [March 20, 2006, 3:40pm UTC](https://www.ruby-forum.com/t/secret-urls-and-file-downloads/84154/1 "2006-03-20T15:40:52Z")

</div>

I’m looking to do something similar to the “secret URLs” in Rails  
Recipes, however, with file downloads. I want to avoid providing direct  
URLs to people if possible.

I have files on disk in public/, so it would be good if each user got a  
“unique” URL to each file that they are permitted to see (dealt with by  
a user\_id/file\_id table). Then they are only allowed to download the  
file if they are logged in and the secret matches. Otherwise, anybody  
could simply point to http://…/example.pdf and download the file,  
whether logged in or not.

I could do this with redirect\_to I believe, but as far as I know that  
just sends a 3xx redirect to the browser which would point to the real  
file.

Is there any easy way to do this?

Cheers.

---

<div class="post-metadata">

**Author:** ![david](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/aeb1de/32.png) [@david](https://www.ruby-forum.com/u/david)\
**Post date:** [March 20, 2006, 4:34pm UTC](https://www.ruby-forum.com/t/secret-urls-and-file-downloads/84154/2 "2006-03-20T16:34:25Z")

</div>

first off, if you don’t want someone to have direct access to the files  
(ie,  
[http://www.yoursite.com/file1.xyz](http://www.yoursite.com/file1.xyz), where fie1.xyz is stored in the  
public  
dir), then don’t store them in public. move them outside of that  
directory  
and store the file information (path, name, etc) in the database.

second, for a simple example, you could store a hash of the  
filename/mtime/etc in the database along with the file information. in  
the  
controller, make sure you have some sort of security in place that says  
the  
person has to be logged in and have access to the file, then you could  
have  
a url something like:

[http://www.yoursite.com/media/download/](http://www.yoursite.com/media/download/)

so you might have:

class MediaController \< ApplicationController::Base  
def download  
media = Media.find\_by\_md5\_hash(@params[:md5\_hash])  
unless media.nil?  
#make sure that the user has permission to get at this file  
send\_file media.filepath  
else  
#oops, no file!  
end  
end  
end

and your route might be:

map.connect “/media/download/:md5\_hash”, :controller =\> “media”, :action  
=\>  
“download”, :md5\_hash =\> /[0-9a-zA-Z]/

---

<div class="post-metadata">

**Author:** ![david](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/aeb1de/32.png) [@david](https://www.ruby-forum.com/u/david)\
**Post date:** [March 20, 2006, 5:24pm UTC](https://www.ruby-forum.com/t/secret-urls-and-file-downloads/84154/3 "2006-03-20T17:24:50Z")

</div>

What if you don’t place them in public and use a pfd controller with a  
send\_data  
action to push the image to the browser…

---

<div class="post-metadata">

**Author:** ![david](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/aeb1de/32.png) [@david](https://www.ruby-forum.com/u/david)\
**Post date:** [March 20, 2006, 6:59pm UTC](https://www.ruby-forum.com/t/secret-urls-and-file-downloads/84154/4 "2006-03-20T18:59:07Z")

</div>

I you use lighttpd, then I would use mod\_secdownload, which does all you  
want and even more (the generated URLs are only valid for a certain  
time,  
you can set)

---

<div class="post-metadata">

**Author:** ![david](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/aeb1de/32.png) [@david](https://www.ruby-forum.com/u/david)\
**Post date:** [March 20, 2006, 8:10pm UTC](https://www.ruby-forum.com/t/secret-urls-and-file-downloads/84154/5 "2006-03-20T20:10:26Z")

</div>

On Mar 20, 2006, at 10:58 AM, Tom M. wrote:

> Never write a line of code if you can avoid it. 🙂  
> Fixing a typo!

…and the hash would end up in params[:id]…

P.S. Do not use @params! That directly accesses the params hash, but  
I’ve heard core  
team members recommend the params method call to insulate  
yourself from possible  
future change to the internal representations.

–  
– Tom M.

---

<div class="post-metadata">

**Author:** ![david](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/aeb1de/32.png) [@david](https://www.ruby-forum.com/u/david)\
**Post date:** [March 20, 2006, 7:58pm UTC](https://www.ruby-forum.com/t/secret-urls-and-file-downloads/84154/6 "2006-03-20T19:58:40Z")

</div>

On Mar 20, 2006, at 7:34 AM, Chris H. wrote:

> access to the file, then you could have a url something like:  
> send\_file media.filepath  
> else  
> #oops, no file!  
> end  
> end  
> end
> 
> and your route might be:
> 
> map.connect “/media/download/:md5\_hash”, :controller =\>  
> “media”, :action =\> “download”, :md5\_hash =\> /[0-9a-zA-Z]/

This is a great solution, IMHO.

Only question is: Why make a new route?

The default route would work just as well, and the hash would end up  
in param[:id]

Never write a line of code if you can avoid it. 🙂

---

<div class="post-metadata">

**Author:** ![david](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/aeb1de/32.png) [@david](https://www.ruby-forum.com/u/david)\
**Post date:** [March 20, 2006, 11:11pm UTC](https://www.ruby-forum.com/t/secret-urls-and-file-downloads/84154/7 "2006-03-20T23:11:59Z")

</div>

On Mar 20, 2006, at 10:58 AM, Tom M. wrote:

> > information. in the controller, make sure you have some sort of  
> > unless media.nil?  
> > map.connect “/media/download/:md5\_hash”, :controller =\>
> 
> –  
> – Tom M.

```
Keep in mind that using send_file like this with large files will ty

```

up one fcgi listsner for each download that is happening for the  
length of the download. SO if you have five users downloading one  
large file each then you will have 5 fcgi processes tied up until  
finished! So mod\_secdownload is a good option to look at if you are  
suing lighttpd. Otherwise you can easily run out of fcgi’s and lock  
up your whole app!

Cheers-  
-Ezra

---

<div class="post-metadata">

**Author:** ![david](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/aeb1de/32.png) [@david](https://www.ruby-forum.com/u/david)\
**Post date:** [March 21, 2006, 1:23am UTC](https://www.ruby-forum.com/t/secret-urls-and-file-downloads/84154/8 "2006-03-21T01:23:30Z")

</div>

never heard of mod\_secdownload before this. neat. much better than my  
suggestion! 🙂

kudos.

Chris

---

<div class="post-metadata">

**Author:** ![david](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/aeb1de/32.png) [@david](https://www.ruby-forum.com/u/david)\
**Post date:** [March 21, 2006, 1:41am UTC](https://www.ruby-forum.com/t/secret-urls-and-file-downloads/84154/9 "2006-03-21T01:41:22Z")

</div>

On Mar 20, 2006, at 4:20 PM, Tom M. wrote:

> > > > information (path, name, etc) in the database.
> > > > 
> > > > end  
> > > > The default route would work just as well, and the hash would end  
> > > > one large file each then you will have 5 fcgi processes tied up  
> > > > backend process quickly.
> 
> Does anybody know how large Lighttpd’s FCGI buffer is, and whether  
> or not it’s configurable?
> 
> –  
> – Tom M.

```
I'm fairly certain that send_file with lighttpd will just ty up the

```

fcgi proc’s but I would love to be proven wrong. I haven’t seen any  
options to configure fcgi buffers anywhere.

-Ezra

---

<div class="post-metadata">

**Author:** ![david](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/aeb1de/32.png) [@david](https://www.ruby-forum.com/u/david)\
**Post date:** [March 21, 2006, 1:20am UTC](https://www.ruby-forum.com/t/secret-urls-and-file-downloads/84154/10 "2006-03-21T01:20:28Z")

</div>

On Mar 20, 2006, at 2:09 PM, Ezra Z. wrote:

> > > second, for a simple example, you could store a hash of the  
> > > def download  
> > > and your route might be:
> 
> suing lighttpd. Otherwise you can easily run out of fcgi’s and lock  
> up your whole app!

If you’re using a front end proxy, this may not be the case.

I seem to remember that Apache+mod\_proxy allows you to specify the  
buffer size, and if the  
buffer was large enough, you could deliver large files into that  
buffer and free up the  
backend process quickly.

Does anybody know how large Lighttpd’s FCGI buffer is, and whether or  
not it’s configurable?

---

<div class="post-metadata">

**Author:** ![david](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/aeb1de/32.png) [@david](https://www.ruby-forum.com/u/david)\
**Post date:** [March 21, 2006, 7:32am UTC](https://www.ruby-forum.com/t/secret-urls-and-file-downloads/84154/11 "2006-03-21T07:32:09Z")

</div>

On Mar 20, 2006, at 4:38 PM, Ezra Z. wrote:

> > > > Only question is: Why make a new route?  
> > > > will ty up one fcgi listsner for each download that is happening  
> > > > buffer was large enough, you could deliver large files into that  
> > > > the fcgi proc’s but I would love to be proven wrong. I haven’t seen  
> > > > any options to configure fcgi buffers anywhere.

I cannot prove this directly, but if Lighty properly implements the  
FCGI spec as recommended, then the FCGI response should be buffered  
by the HTTP server:

from: [http://www.fastcgi.com/devkit/doc/fcgi-spec.html](http://www.fastcgi.com/devkit/doc/fcgi-spec.html)

---

<div class="post-metadata">

**Author:** ![david](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/aeb1de/32.png) [@david](https://www.ruby-forum.com/u/david)\
**Post date:** [March 22, 2006, 1:05am UTC](https://www.ruby-forum.com/t/secret-urls-and-file-downloads/84154/12 "2006-03-22T01:05:49Z")

</div>

I’ve written an RSS feed parser/downloader using Ruby and ActiveRecord  
and  
http-access2 without Rails. I want this code to always be running in a  
loop and downloading feeds on a schedule. I started by coding the  
back-end (downloading feeds and putting the titles, links, and  
descriptions into a database). Now, I’m wondering, is there an easier  
way…

I suppose it is possible to invoke this code as needed when a user loads  
a  
page, however, it seems like it’d be easier if there was a rails  
front-end  
that managed the feed urls, but not the actual feed data itself.

I guess my question is, can a pure ruby process to handle this perpetual  
munging of rss data be spawned from within rails? Or is it better to  
just  
spawn this back end code and run a database updater in an infinite loop?  
Does anyone know how Odeo handles this?

Tony  
[http://involution.com](http://involution.com)

---

<div class="post-metadata">

**Author:** ![david](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/aeb1de/32.png) [@david](https://www.ruby-forum.com/u/david)\
**Post date:** [March 22, 2006, 12:45am UTC](https://www.ruby-forum.com/t/secret-urls-and-file-downloads/84154/13 "2006-03-22T00:45:03Z")

</div>

Chris H. wrote:

> first off, if you don’t want someone to have direct access to the files  
> (ie,  
> [http://www.yoursite.com/file1.xyz](http://www.yoursite.com/file1.xyz), where fie1.xyz is stored in the  
> public  
> dir), then don’t store them in public. move them outside of that  
> directory  
> and store the file information (path, name, etc) in the database.
> 
> second, for a simple example, you could store a hash of the  
> filename/mtime/etc in the database along with the file information. in  
> the  
> controller, make sure you have some sort of security in place that says  
> the  
> person has to be logged in and have access to the file, then you could  
> have  
> a url something like:
> 
> [http://www.yoursite.com/media/download/](http://www.yoursite.com/media/download/)
> 
> so you might have:
> 
> class MediaController \< ApplicationController::Base  
> def download  
> media = Media.find\_by\_md5\_hash(@params[:md5\_hash])  
> unless media.nil?  
> #make sure that the user has permission to get at this file  
> send\_file media.filepath  
> else  
> #oops, no file!  
> end  
> end

Thanks very much for this. The site will be very low usage, so it  
doesn’t matter too much if the process is tied up for a while. I will  
investigate the other modules suggested too, to see whether they’re easy  
enough to use for the site.

Thanks to everybody who responded,

David

---

<div class="post-metadata">

**Author:** ![david](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/aeb1de/32.png) [@david](https://www.ruby-forum.com/u/david)\
**Post date:** [March 25, 2006, 5:17am UTC](https://www.ruby-forum.com/t/secret-urls-and-file-downloads/84154/14 "2006-03-25T05:17:53Z")

</div>

On 3/21/06, Tony P. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> I guess my question is, can a pure ruby process to handle this perpetual  
> munging of rss data be spawned from within rails? Or is it better to just  
> spawn this back end code and run a database updater in an infinite loop?  
> Does anyone know how Odeo handles this?

I think that the wiki page  
[http://wiki.rubyonrails.com/rails/pages/HowToRunBackgroundJobsInRails](http://wiki.rubyonrails.com/rails/pages/HowToRunBackgroundJobsInRails)  
can help you some.

> Tony  
> [http://involution.com](http://involution.com)

Sincerely,

Tom L.  
[http://AllTom.com/](http://AllTom.com/)  
[http://GadgetLife.org/](http://GadgetLife.org/)

---

<div class="post-metadata">

**Author:** ![david](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/aeb1de/32.png) [@david](https://www.ruby-forum.com/u/david)\
**Post date:** [September 1, 2006, 4:30pm UTC](https://www.ruby-forum.com/t/secret-urls-and-file-downloads/84154/15 "2006-09-01T16:30:20Z")

</div>

I may be missing something but here is how I may be solving a similar  
problem for an upcoming problem. Downloading files will be a large part  
of my application.

Have two web servers, mapped to different IP’s.

The application server  
IP: 192.168.2.1  
[myapp.mydomain.com](http://myapp.mydomain.com)  
will use one of the yet-to-be-determined scalable Rails implementations

The download server  
IP: 192.168.2.2  
Will use IIS or Apache

I’ll use one of the Apache plugins to “expire” certain downloads or, in  
the case of IIS, will write a simple scheduled task to crawl the site  
and delete old files.

For my purposes, I’m finding it easier to manage downloads on a separate  
non-Rails server.

---

<div class="post-metadata">

**Author:** ![david](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/aeb1de/32.png) [@david](https://www.ruby-forum.com/u/david)\
**Post date:** [September 1, 2006, 3:38pm UTC](https://www.ruby-forum.com/t/secret-urls-and-file-downloads/84154/16 "2006-09-01T15:38:56Z")

</div>

If you’re using Apache, mod\_auth\_token supports the same functionality  
and interface as mod\_secdownload, eg. allowing you to secure and expire  
downloads  
without having to pipe the file through your script.

[http://www.synd.info/downloads/releases/](http://www.synd.info/downloads/releases/)

//Mikael

David wrote:

> I’m looking to do something similar to the “secret URLs” in Rails  
> Recipes, however, with file downloads. I want to avoid providing direct  
> URLs to people if possible.
> 
> I have files on disk in public/, so it would be good if each user got a  
> “unique” URL to each file that they are permitted to see (dealt with by  
> a user\_id/file\_id table). Then they are only allowed to download the  
> file if they are logged in and the secret matches. Otherwise, anybody  
> could simply point to http://…/example.pdf and download the file,  
> whether logged in or not.
> 
> I could do this with redirect\_to I believe, but as far as I know that  
> just sends a 3xx redirect to the browser which would point to the real  
> file.
> 
> Is there any easy way to do this?
> 
> Cheers.

---

<div class="post-metadata">

**Author:** ![david](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/aeb1de/32.png) [@david](https://www.ruby-forum.com/u/david)\
**Post date:** [September 1, 2006, 6:48pm UTC](https://www.ruby-forum.com/t/secret-urls-and-file-downloads/84154/17 "2006-09-01T18:48:50Z")

</div>

sorry for the noob question, but when you say “logged in” all that means  
is that you have a session file that is insync with the rails  
application?
