I'm trying to do a cache system with files. And I was thinking of using mod rewrite to serve the cached file if it exist, or the php file (that will create the html static cache) if not.

Here is the first sample I came up with

         <IfModule mod_rewrite.c>
            RewriteEngine On

            RewriteRule ^$ /cache/home.html [L,NC]
            RewriteCond %{REQUEST_URI} /cache/
            RewriteCond %{REQUEST_FILENAME} !-f
            RewriteRule ^cache/home.html /index.php [L]

            RewriteRule ^page/([0-9]*) /cache/page_$1.html [L,NC]
            RewriteCond %{REQUEST_URI} /cache/
            RewriteCond %{REQUEST_FILENAME} !-f
            RewriteRule ^cache/page_([0-9]*).html /page.php?&id=$1 [L]

This works correctly for the internal pages. If I call http://www.example.site/page/1 or http://www.example.site/page/2 it correctly serves the cached file or the php page depending if the file exists or not.

The problem is that it doesn't work for the main site http://www.example.site

If I add a redirect flag for the home it actually works correctly

RewriteRule ^$ /cache/home.html [L,NC,R]

But, obviously, in this case if I call the main site and I am served with a http://www.example.site/cache/home.html which is not what I was looking for.

Here are a couple of lines of log where the problem might be:

[Wed Aug 05 11:59:11.751277 2015] [rewrite:trace1] [pid 30670] mod_rewrite.c(468): [client] - - [m.site.com/sid#7f7850ecb038][rid#7f7850e480a0/initial] [perdir /home/www/site.com/public/mobile/] internal redirect with /cache/home.html [INTERNAL REDIRECT]
[Wed Aug 05 11:59:11.751339 2015] [rewrite:trace3] [pid 30670] mod_rewrite.c(468): [client] - - [m.site.com/sid#7f7850ecb038][rid#7f7850e420a0/subreq] [perdir /home/www/site.com/public/mobile/] strip per-dir prefix: /home/www/site.com/public/mobile/index.html -> index.html

While with the R flag

[Wed Aug 05 12:04:34.333591 2015] [rewrite:trace1] [pid 31746] mod_rewrite.c(468): [client] - - [m.site.com/sid#7f7850ec1038][rid#7f7850e440a0/initial] [perdir /home/www/site.com/public/mobile/] redirect to http://m.cmstest.site.com/cache/home.html [REDIRECT/302]
[Wed Aug 05 12:04:34.343609 2015] [rewrite:trace3] [pid 31746] mod_rewrite.c(468): [client] - - [m.site.com/sid#7f7850ec1038][rid#7f7850e420a0/initial] [perdir /home/www/site.com/public/mobile/] strip per-dir prefix: /home/www/site.com/public/mobile/cache/home.html -> cache/home.html

It seems that the first one, without the R flag, after doing the correct INTERNAL REDIRECT to cache/home.html it goes to look for index.html (that doesn't actually exist)

  • What do you mean doesn't work? Subsequent rules match that you don't expect? There is an [END] flag that causes per-directory rewriting to be "really [L]ast" – covener Jul 31 '15 at 23:30
  • The root without the R flag doesn't work. – Kesty Aug 2 '15 at 13:14
  • Have you verified that the context that you are attempting to match the pattern against will yield the correct results? Based on the documentation it seems like the behavior you are describing might be correct for the directory context. – Matt Aug 3 '15 at 7:20
  • What do your rewrite logs say? – Jenny D Aug 5 '15 at 7:32
  • Hi @JennyD, I've looked at the log rewrite again. Added some lines to the post. It seems as if it actually find and does an internal redirect (at least the log says it does), but then actually goes to look for index.html for some reason. – Kesty Aug 5 '15 at 10:22

Your first rule says:

RewriteRule ^$ /cache/home.html [L,NC]

The L flag tells mod_rewrite to stop processing at that point, which is the problem you're asking about. The 'R' workaround means that the new rule gets rewritten as expected, on the second request, but the L prevents it on the first. Also, the NC flag is redundant here, since there can be no path characters to be affected by the case sensitivity.

While not part of your question, also reconsider the following.

RewriteRule ^cache/home.html /index.php [L]

You probably want to match with ^cache/page.html$, so that it doesn't accept cache/home.htmlxyz, the the practical impact is likely to be minimal. Also there's the rule:

RewriteRule ^page/([0-9]*) /cache/page_$1.html [L,NC]

The flags are probably inappropriate as before. The NC flag actually does something here, but I'd argue that it's undesirable. Also consider that the numerical sequence in your regex can be of zero length, which I suspect is not desirable, so maybe you want ([0-9]+)? You probably also want to terminate that with a '$', as at present you will match e.g. /page/foobar, and you also don't want to match /page/123foobar. So you'd have:

RewriteRule ^page/([0-9]+)$ /cache/page_$1.html


See comments below about why matching ^$ might not work for you. Also, if fixing that doesn't work, try adding mod_rewrite:trace3 to your LogLevel directive, and work out exactly where it's going wrong. The output is verbose, but you'll learn a lot, and if you're still puzzled, you'll have more detail to bring back to us.

  • Even if I write the first rule as RewriteRule ^$ /cache/home.html without any flag it still doesn't work. – Kesty Aug 5 '15 at 9:31
  • The server would normally want to rewrite ^$ to ^/$ (not using mod_rewrite). I'm not sure if mod_rewrite takes precedence here. You really want to match whether the / is there or not, so try using RewriteRule ^/?$ /cache/home.html – mc0e Aug 5 '15 at 16:23
  • Did you try this last variant? – mc0e Aug 7 '15 at 8:26
  • The log snippet implies these rules are in htaccess, so there will never be a leading slash to match. – covener Aug 9 '15 at 14:00

The problem seems to be with the subrequests, like in this answer: https://stackoverflow.com/questions/9618865/mod-rewrite-mysterious-subreq

As seen from the log, after the first correct Internal Redirect, the second call is to a subrequest that tries to find an index.html in the root directory.

I've tried to add the option -MultiViews as in the answer I linked but that still didn't work. Apache was still loking at subrequest in the website root.

So i ended up with a small "hack" solution that will check if the request is a subrequest and in that case if it's looking for an index.* file in the root it will redirect to /cache/home.html

        RewriteCond %{IS_SUBREQ} t
        RewriteCond %{REQUEST_URI} ^(/index.\w+|/)?$
        RewriteRule ^ /cache/home.html [L]

This works fine, in the sense that ends up doing exactly what I was trying to do.

I'll let the question open for a while longer, because this still feels like an "hack" solution. Don't know if there is a better one that should work with directly blocking those subrequests.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.