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.
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!