Control Cache Expiration For Custom File On Server - apache

We have files that we serve to a Native Windows OS applications from our server. The files can change every minute so we need to ensure the user is downloading the latest file.
We've found that users on Portable WiFi's tend to get served an older file. So we are changing our servers .htaccess file expirations for certain files.
We serve a custom file type (.ebc) and the files contents are sent over HTTP as plain text. In this case should we use ExpiresByType text/ebc "access 1 minute"?
Will changing .htaccess cache control affect Portable Wifi caching or will this only affect browsers?
Should mod_expires / mode_headers occur code occur before redirects and rewrites? I've discovered before that you should perform certain .htaccess code operations before others (such as place redirects at the top of the file).
Heres my code:
RedirectMatch (?i)^/wp-content/uploads/2014/10/a.exe http://www.website.com/wp-content/uploads/2014/10/b.exe
## EXPIRES CACHING Should we place this before mode_rewrite or after? ##
<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType text/ebc "access 1 minute"
</IfModule>
## EXPIRES CACHING ##
# BEGIN WordPress
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>
# END WordPress

There are 3 questions here, so I'll attempt to answer them.
We serve a custom file type (.ebc) and the files contents are sent over HTTP as plain text. In this case should we use ExpiresByType text/ebc access 1 minute
That should be fine, as long as you have the text/ebc mime-type properly set on your server.
Will changing .htaccess cache control affect Portable Wifi caching or will this only affect browsers
I don't really know what "Portable Wifi caching" is. These headers are targeted at browsers only. If a custom application is downloading these files, it could be implementing its own caching and so these headers might get ignored.
Should mod_expires / mod_headers code occur before redirects and rewrites?
I'd put it before the redirects but only from a logical point of view. These are not like RewriteRules and think they get evaluated separately.
Additionally, I'll add that caching is difficult and once a file has left your server it can be hard to force an update. Different browsers behave different ways and I've come across configurations that work one place and not another.
I would additionally consider two other approaches to what you're attempting.
Firstly, don't cache your files at all:
<FilesMatch "\.ebc$">
Header set Cache-Control no-cache
Header set pragma no-cache
</FilesMatch>
Secondly think about implementing a cache-busting mechanism. If the file is linked from somewhere, try and make sure that link is changed (normally a querystring with a timestamp suffices) each time the file changes. You obviously then need to make sure whatever contains the link also isn't being cached.

An easier solution I used in the past was adding a parameter to the downloadable files.
For example, if the file you're serving is
http://www.domain.tld/file.pdf
then you can create the following link:
http://www.domain.tld/file.pdf?d486dFyg
The question mark and whatever comes after it (random) will be ignored but it will guarantee that the user will always download the latest version, as the URL will be different (because of the random always being different of course).
The downloaded file on the user's computer will just be file.pdf so absolutely no downside.
EDIT: I noticed some reference to WordPress in your question, which is PHP, so you can use the rand() function to append the random part: http://php.net/manual/en/function.rand.php

Related

$_GET["foo"] NULL from RewriteRule

I am trying to redirect http://localhost/tour/hello to http://localhost/tour.php?name=hello
I have tried the following in my .htaccess
RewriteEngine on
RewriteCond %{REQUEST_URI} ^/tour/(.+)/?$
RewriteRule ^tour/(.+)/?$ tour.php?name=$1 [L,QSA, NC]
It redirects to http://localhost/tour.php but $_GET["name"] is always NULL
if I change the target page to anything other than tour.php it works
RewriteRule ^tour/(.+)/?$ handler.php?name=$1
RewriteRule ^tour/(.+)/?$ blah.php?name=$1
All work
You need to disable content negociation by:
Options -MultiViews
In your .htaccess.
And AcceptPathInfo off.
Some explanations:
The first issue is content negociation. Say you have two files named tour.txt and tour.php, if content negociation is enabled, if your URL is just http://localhost/tour, without extension, Apache is looking for files named tour with any extension and based on client preferences (in particular the Accept HTTP header for this example), will try to find the best match among tour.txt and tour.php to serve to the client.
The second element with your issue is AcceptPathInfo: when enabled, Apache accepts the superfluous part at the end of the path of the URL to populate it as an internal variable (the well known $_SERVER['PATH_INFO'] in PHP for example). To illustrate, let's say you have a file tour.php. So, with AcceptPathInfo on, http://localhost/tour.php/extra/path invokes tour.php with $_SERVER['PATH_INFO'] = /extra/path instead being considered as inexistant (404) as usually expected/with AcceptPathInfo off.
Now, combine both, AcceptPathInfo and content negociation: the problem is that your rule try to "intercept" what begins with tour/ but tour.php exists [is a file] so it conflicts with your own rule since the path tour/foo [for the URL http://localhost/tour/foo] is first resolved by the content negociation as tour.php/foo and "accepted" as tour.php with $_SERVER['PATH_INFO'] = /foo thanks to AcceptPathInfo. Conclusion: in this very specific case, no rewriting happened but it looks so because of content negociation + AcceptPathinfo which kind have the same effect by landing on tour.php and this is why you don't get the query string you were expecting (name=foo).
Also note there shouldn't be any space before NC flag in your rule.

How to setup static assets caching with apache?

I'd like to optimize caching of static assets (.js, .css, ... files) used in our web. My goal is based on this article (https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-caching#invalidating-and-updating-cached-responses).
In short - because these static assets tend to be updated ad-hoc (sometimes weekly, sometimes twice a day, ...) I'd like to cache them with far future expiration and give them unique names based on the content or modification date or similar. This should allow to have them cached for a long time but have them updated as soon as some change occurs.
Is this technique supported by Apache2 server? Or is there some middle ware system which handles fingerprints generating (to have unique asset names) and updating references to them in HTML file (which won't be cached at all)?
We use LAMP stack on our host.
Thank you in advance
There are a number of techniques, some better than others. One good one is to have the following configuration:
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.+)\.(\d+)\.(bmp|css|cur|gif|ico|jpe?g|js|png|svgz?|webp|webmanifest)$ $1.$3 [L]
</IfModule>
This allows URLs of the form /i/filename.1433499948.gif - but the file that is actually read from disk is just /i/filename.gif parts 1 and 3 of the filename.
This Apache vhost/.htaccess stanza is from H5BP filename-based_cache_busting.conf file, and there are other examples of good practices in the repository.
That, combined with the H5BP mod_expires config, mean you will always be able to trivially renew the users local browser cache with just updating the reference to the file by a new name.
You can enable mod_mime, mod_expires for Apache and use the following snippet
<FilesMatch "\.(png|jp?g|gif|ico|mp4|wmv|mov|mpeg|css|map|woff?|eot|svg|ttf|js|json|pdf|csv)">
ExpiresActive on
ExpiresDefault "access plus 2 weeks"
</FilesMatch>
Or set the respective php headers
session_cache_limiter('none');
header('Cache-control: max-age='.(60*60*24*7)); //one week
header('Expires: '.gmdate(DATE_RFC1123,time()+60*60*24*365)); //one week
Also related article here: How to get the browser to cache images, with php?

Manual content compression in Apache

I need a manual compression solution on Apache. My goals:
Provide gzip-encoded content on my server along with uncompressed.
Files are pre-compressed.
Not all files are served compressed. I want to specify these files and the choice isn't type (extension) based.
Many content-types (custom ones) are served, and new types are showing up from time to time. Also, file extension doesn't determine if it will be compressed or not (!!!).
Keep overhead minimal (the less extra headers, the better).
Always provide Content-Length header and never send chunked response (this disqualifies mod_deflate).
Ideal Functionality
Ideal functionality would work like that:
Web client asks for file file.ext.
If file.ext.gz exists on server:
Content-Encoding is set to gzip.
Content-Type is set to value of file.ext (!!!).
Server returns file.ext.gz.
Otherwise, file.ext is returned.
I tested a number of solutions, this article contains a good compilation, but there was always a problem with parts marked with (!!!). I have hundreds of thousands of files and dozens of content types and because of that I'm looking for some automatic solution, without need of adding ForceType for each file.
What I tried
Multiviews
How this works:
Rename a file file.ext to file.ext.en
Create a file file.ext.gz
Configure Apache:
Options +MultiViews
AddEncoding x-gzip .gz
RemoveType .gz
Works almost as expected, except it requires the original file (file.ext) to not exist and it adds many (useless to me) headers (TCN, Content-Language and few more) that can't be removed (Header unset doesn't remove them).
Rewrite
How this works:
- Create file.ext.gz file.
- Configure Apache:
<pre>
RewriteEngine On
RewriteCond %{HTTP:Accept-Encoding} gzip
RewriteCond %{REQUEST_FILENAME}.gz -f
RewriteRule (.*)$ $1.gz [L]
AddEncoding x-gzip .gz
<Files file.ext>
ForceType my-custom/mime-type
</FilesMatch>
</pre>
This works well, but requires a ForceType for each compressed file. As I said before, I can't rely on extensions because not all files of certain type will be compressed.
mod_deflate
I didn't investigate it too much, the problem is that if file is too big, then it's split into pieces and sent in chunks and Content-Length is not provided. Increasing size of compression buffers can't eliminate this problem.
Is it possible at all to configure Apache to work as I'd liked to?
I tried to dynamically get Content-Type of file.ext and set it to file.ext.gz, but I didn't find the way how to do this.

Use .htaccess to whitelist two files for execution

I have a website that has a folder for images.
I have two problems:
I want to disable all script execution in that directory (i.e. no PHP/Perl/Python
anything.)
There are two php files in my images folder called gradient.php and rgba.php – I do what those to run as per usual.
How do I set up my .htaccess file to do that. Also, rather than placing a new .htaccess in the images directory, is it possible to incorporate these directives in the one in the site root?
You can add these rules to the htaccess file in your site root:
# check if the request is for the images folder
RewriteCond %{REQUEST_URI} ^/images/
# check that it isn't a request for an image
RewriteCond %{REQUEST_URI} !\.(jpe?g|png|gif|bmp)$ [NC]
# check that it isn't a request for the 2 ok php files
RewriteCond %{REQUEST_URI} !^/images/(gradient|rgba)\.php$
# forbid access
RewriteRule ^ - [L,F]
This should make it so any request for /images/ that doesn't end with jpg/jpeg/png/gif/bmp (or whatever other extension you want to add to the regular expression) or isn't gradient.php or rgba.php, will result in a 403 forbidden.
EDIT:
I don't want them to be forbidden, I just want them to not execute – it's an upload folder, so I basically want it that if someone uploads a JPG that is secretly PHP code that I haven't detected, that it won't run
as long as jpg and other images are mapped to the correct mime/type (via AddType image/jpeg .jpg) then it won't get handed to the php handler and whatever code is there won't get executed. If you want to serve all files using the default handler, you need to set AddHandler default-handler php in the htaccess file in your images directory. You'll then need to move the gradient and rgba files out to some other directory. You can't selectively set handlers from an htaccess file, though you may be able to use <Location> blocks to set handlers in your vhost config.
EDIT 2:
I was wrong, you can use the H flag to set a custom handler using a rule. So in the above rules, instead of [L,F], you can do [L,H=default-handler] so that anything that isn't an image or gradient.php or rgba.php will get sent to the default-handler (e.g. php files will get sent as-is, and not handled and executed by mod_php).
So you can just do:
RewriteEngine On
RewriteCond %{REQUEST_URI} !^/images/(gradient|rgba)\.php$
RewriteRule ^ - [L,H=default-handler]

How to prevent Apache / mod_rewrite from treating path as file with same name

I'm using WAMP Server, mostly configured as-is out of the box. I'm having trouble getting mod_rewrite to behave as expected locally (everything works fine on a production server).
I have a PHP file located at:
/ajax/graphs/get-graph.php
The way this file is normally invoked is via a bootstrap file loaded by
/index.php
I have a .htaccess file at the root with the following rules:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ /index.php [L]
So, basically, when my app requests via AJAX a call to /ajax/graphs/get-graph/it should be directed to /index.php.
The problem is, Apache/mod_rewrite sees the request path and loads /ajax/graphs/get-graph.php directly.
How do I prevent Apache from assuming that /ajax/graphs/get-graph/ is a valid file because a php file of the same name exists at that location?
It sounds like you've fallen into the trap of content negotiation ;-) As explained in the Apache documentation, there is an option called MultiViews which, when enabled, causes Apache to basically convert nonexistent directory names into their corresponding filenames.
The effect of MultiViews is as follows: if the server receives a request for /some/dir/foo, if /some/dir has MultiViews enabled, and /some/dir/foo does not exist, then the server reads the directory looking for files named foo.*, and effectively fakes up a type map which names all those files...
The intent is that you can have several versions of a file in different formats or languages, like
/some/dir
- foo.en.gif
- foo.en.png
- foo.en.jpg
- foo.fr.gif
- foo.fr.png
- foo.fr.jpg
and Apache will choose the best one based on the preferences provided by the browser.
To fix it, all you should need to do is add the directive
Options -MultiViews
in a <Directory> or <Location> block corresponding to /ajax/graphs. Or, if you don't have access to the main server configuration, you can put it in /ajax/graphs/.htaccess.