Force a PDF to download

Apache Logo

I recently needed to force a PDF to download using Apache. The default behaviour for most browsers is to try to open the PDF inside the browser itself. This is fine for a small PDF or for powerful machines - but a large PDF on even a modest machine can often lock the browser up. This needed fixing!

After 20 minutes of perusing the Apache documents, I happened across the FilesMatch option which takes Regular Expressions. Regular Expressions are cool things which pattern match; you give it a rather complicated (yet logical) pattern and it matches it for you. Initially I used something like this...

<Files *.pdf>
  ForceType application/pdf
  Header set Content-Disposition attachment
</Files>

This worked PERFECTLY - except some files had upper-case extensions and some had lower and I could see situations in the future where combinations of upper and lower case would be used too - just to piss me off! Because of this, not even this would work...

<FilesMatch "\.(pdf|PDF)">
  ForceType application/pdf
  Header set Content-Disposition attachment
</FilesMatch>

That would match perfectly - as long as it was an EXACT match on upper OR lower case.

I was reaching the end of my patience - that is until I read the Using Character Classes on PerlDoc.

This showed me that I could force the RegEx (short for Regular Expressions) to match in a case-insensitive manner. This lead me to the following...

<FilesMatch "\.(?i:pdf)$">
  ForceType application/pdf
  Header set Content-Disposition attachment
</FilesMatch>

However this only worked in proper browsers - and the bulk of the world are sadistic enough  to use Internet Explorer based ones. For some reason, if Internet Explorer see's the content type "Application/PDF" it will simply open it up in the reader. The solution? Why not pretend its a bog standard Octet Stream, just like a Zip file? After all, that's basically all it is; a binary file... A steam of bytes.

<FilesMatch "\.(?i:pdf)$">
  ForceType application/octet-stream
  Header set Content-Disposition attachment
</FilesMatch>

And there you have it… A perfectly working modification to force all PDF files to download - this will work for any file extensions you chose to put into the FilesMatch argument!

Comment Icon

47 Comments

The most recent comment was on Wed, 14th Mar 2012 - 20:54

Hey! That worked perfectly, thanks! You might indicate that one needs mod_headers to use Header set.

Thanks for that... I didn't have a problem as my Apache configs tend to have everything enabled by default ;-)

Hey, great explanation. Thanks for the well written explanation and for the code sample(s).

Really Interesting document, but how would you use that if apache is a proxyserver?

Hi,

I can't get this to work in .htaccess, I asked around and someone said that the regex was wrong.

Any idea how I can get it to work?

I've been trying to do this for 6 months. Worked perfectly. I love you, sir (platonically)

This is just what i was lokking for! Thanks for the explanation! I will be linking to your post :-)

Thanks, the Internet Explorer specific Header Set and RegEx tip really helped!

I just copy and paste to .htaccess file, but getting internal error. Do you have any idea what would be wrong?

You'd probably need to check your error logs. Maybe an apache extension isn't enabled?

I try to use this trick to open a download window from the browser instead of displaying an image.

<Files *.*>
  ForceType applicaton/octet-stream
  Header set Content-Disposition attachment
</Files>

it works in Mozilla, but not on IE. the mod_headers module is enabled ...

I'm horrible at regular expressions; How could I modify that to force download of all file types? I have a folder full of different file types for clients to download, and this would save them time.

You'd simply put the 2 main lines into a .htaccess file in that folder; no need to files match. Bear in mind, and HTML files in there (such as index.html) may also start to download to...

NOTE: I've not tried this - it's an educated guess...

Thanks for solution,
regular expression \.[Pp][Dd][Ff] also can be used

Thanks for this tip, works perfectly in everything but safari.. looking for a solution to that problem now..

PS. Drupal rocks!

I run the IT direction for several companies and occasionally need to offer files for download without going through the hassle of creating stub pages, or explaining (again!) to users that email isn't the best place to attach a 10MB file.

I stopped by to mention that the code above also works quite well within Apache VirtualHosts directives. Simply include the block above inside the appropriate virtual host, such as "download.mydomain.com" and you can serve your files via HTTP, leaving the rest of your subdomains (such as "www") to serve the content you have time to write.

One quite valuable side-effect is that this also allows administrators to split download functions to another server (read: faster connection) when demand surpasses capacity...without rewriting everything you've done to provide downloadable content.

To wit, thank you; you saved me enough research time that I could stop to...hopefully :)...add value to your writeup. Cheers!

First of all FilesMatch does not work in older versions of Apache.

Secondly, it is far easier to alter the mime types. Just add this to your .htaccess file:

AddType application/octet-stream .pdf

Is it possible that this doesnt works with IE6?

If you search the net there have been known issues with ie6 not setting the headers correctly. Nick, did't we come across this issue with some of the websites at ew? I'm sure this rings a bell with the conference team saying they had users that couldn't download pdfs and i'm sure they were IE6 users.

Yeah I do recall IE6 having problems with it. It's always a safe assumption that if a visitor has a problem, they're using IE6...

Thanks just what I was looking for!!!

I was so excited to come across this, but then I couldn't get it to work for what I need to do :( I imagine this is just me doing something wrong or inadvertently doing something else that's impeding the effectiveness of this, but... here goes...

I'm actually trying to do the opposite, client doesn't like the "open/save" dialog, wants to eliminate this "extra step" to view the document (granted, the primary purpose of the website in question is to organize & view files). I looked around and came up with "inline" as the alternative to/opposite of "attachment" -- is this the case, and if not, what is?

The whole thing is really weird -- certain PDFs on this website open right in the browser the way they want, but the vast majority don't. (When I say "open right in the browser" what I actually mean is "follow your browser settings, like in Firefox whatever you set at Tools > Options > Applications.) I even tested a PDF file from a different website I manage -- this file opens in your browser when you click on a link to it from the other website, but when I put it on my problem-website, I end up with the "open/save" dialog. Gaaahhhh!!

If nothing else, can anyone offer advice to help me figure out WHAT is affecting this behavior, and/or HOW to tell whether or not I've used the FilesMatch thing correctly (so at least I can know if/when to stop trying this and look for something else)?

Some details about my problem-website that may be important -- it's on Drupal 6.15, it's a password-protected site and I'm using the Private Download module. The Apache version is 2.2, with PHP 5.2.13 and MySQL 5.0.77.

I greatly appreciate any help you can offer -- but of course, if this is totally beyond what you had in mind for the scope of this, just say the word :) Thank you!

The browser will use the content type mime header to determine how to deal with the content, hence the "ForceType" in the htaccess rule above. If the browser doesn't recognise the MIME type as something is can natively "deal with" (like PDF or HTML), then it will prompt to download it.

The issue may be the Private Download module - maybe is just sending a generic header to force the download?

(a) Never mind.......... while I'm not certain that I updated my .htaccess file(s) correctly, I am certain that other "things" (in this case, the Private Download module) were in the way of the .htaccess fix working anyway.

(b) I ended up learning a tiny bit more about file headers that I will share just in case it's useful for anyone: Alternative syntax for setting file header attributes, depending on where you're doing it, goes something like:
Content-Disposition: inline

(In case some context is helpful...) In my situation, I set this attribute right after the two file header attribute settings that the module included by default, Content-Transfer-Encoding: binary\nCache-Control: max-age=60, must-revalidate

Maybe someone could offer an explanation of what one would "call" this situation -- like, to fill in the blanks of a sentence such as "Here's how to set file header attributes -- if [blank], do it like [this], if [blank], do it like [this]..."

Thanks everyone, and sorry if all of this was "outside the scope."

Hi Nick -- Thanks so much for your answer, I JUST posted a reply to myself like 10 seconds ago, apparently I should have refreshed the page before doing so :)

If you have another minute, I'd greatly appreciate your thoughts on what ended up fixing my issue -- it sounds like what I did could have been the "sending a generic header to force the download" you suggested?

Thanks again for your help!

Hi,
Some things to bare in minds:


  1. By default, headers will overwrite previous entries with the same key.

  2. A "generic" download would be octet-stream, as demonstrated with my last example in the blog post.

  3. Some browsers dont always interpret headers correctly (read: IE doesn't always interpret headers correctly)

Hope this helps :-)

The Content-Disposition Header could cause problems in IE when leading the user to the file with a window.open() and since we are already forcing type octet-stream it will already force the browser to download the file since no browser will try to show a octete-stream to the user.

<FilesMatch "\.(?i:pdf)$">
  ForceType application/octet-stream
</FilesMatch>

For some reason this doesn't seem to work in the current Dev versions of Google Chrome (11.0.696.14). I am not sure why. Do you know of any other way to fix this? Thanks!

Doesn't Google Chrome handle PDF's 'internally' (rather than other browsers using an external app to view them). Maybe it bases it's decision on the filename/extension rather than the Header MIME type?

Federico Gonzalez Brizzio's picture

Hi,

Some people directly use:
AddType application/octet-stream .pdf

Inside htaccess. Do you know wich's better?

Thanks!

HTAccess files get read and processed on every request, whereas Apache configs are processed once on startup, therefore Apaceh configs are more efficient, in theory. It does mean any changed require Apache to be restarted though...

Aweseome this worked perfectly! x

Many many thanks for posting this! It works on IE, Safari, Chrome and Firefox here!
Force download through .htaccess doesn't work on IE, so you have to edit httpd.conf.

Many thanks for posting this - saved me a long trawl through Apache docs!

Works perfect with different Mac OS X browsers - but it doesn't work with iPhone 4S & Safari. Any Idea?

thank you very much, i've not tried on IE yet, but it works just fine with other browsers

i used to compress the pdf's and link them after that, thanks for the tip!

Add new comment

Filtered HTML

  • Allowed HTML tags: <a> <em> <strong> <cite> <code> <pre> <ul> <ol> <li> <dl> <dt> <dd> <img> <p>
  • You can use BBCode tags in the text. URLs will automatically be converted to links.
  • You can enable syntax highlighting of source code with the following tags: <code>, <pre>, <bash>, <css>, <html>, <js>, <jquery>, <mysql>, <php>. PHP source code can also be enclosed in <?php ... ?> or <% ... %>.

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
By submitting this form, you accept the Mollom privacy policy.