Apache: apply rules to a URL before they're rewritten - apache

I have a simple RewriteRule:
RewriteRule ^/r/[0-9]+/(.*)$ /$1
This is used for cache-busting. With every web site release I change the url prefix, e.g.:
/r/17/img/image.jpg gets /img/image.jpg.
I want to apply long expiry headers to these for example
<Directory /r>
Header unset ETag
FileETag None
ExpiresDefault "access plus 1 year"
</Directory>
Of course this doesn't work because after the RewriteRule is applied, the Directory doesn't match anymore.
How can I apply these rules inside the Directory directive to URLs accessed via /r/ ?
Thanks!

The <Directory> directive is for actual existing directories and not just URL paths. Try <LocationMatch> instead:
<LocationMatch "^/r(/|$)">
Header unset ETag
FileETag None
ExpiresDefault "access plus 1 year"
</LocationMatch>
Or change /r to your actual directory /img.

Related

htaccess: mod_expires.c except one or multiple folders

To the following browser caching via mod_expires.c in the .htaccess...
<IfModule mod_expires.c>
ExpiresActive On
ExpiresDefault "access plus 1 day"
</IfModule>
...I would like to add an exception: One or more folders shall not be cached. I tried a version with Directory before </IfModule>, but that led to a 500 Internal Server Error. That
<Directory "/absolute/path/to/specialfolder">
ExpiresActive On
ExpiresDefault "access plus 1 seconds"
</Directory>
and that snippet
<Directory "/absolute/path/to/specialfolder">
ExpiresActive Off
</Directory>
What's wrong and what could help? (for one or a few folders)
The <Directory> directive is not permitted in a .htaccess context, it can only be used in a server (or virtualhost) context. (Hence the 500 error.)
The userland/.htaccess way would be to create another .htaccess file in that "specialfolder" with ExpiresActive Off in order to override the parent config.
Alternatively, you could perhaps use an <If> expression in the root .htaccess file.
For example:
# Turn off mod_expires for a specific directory
<If "%{REQUEST_URI} =~ m#^/specialfolder/#">
ExpiresActive Off
</If>
Where REQUEST_URI is the document-root-relative URL-path.
OR, only use mod_expires when not requesting these folder(s). For example:
# Only use mod_expires when NOT requesting the specific directories
<If "%{REQUEST_URI} !~ m#^/(specialfolder|anotherfolder)/#">
ExpiresActive On
ExpiresDefault "access plus 1 day"
</IfModule>
One or more folders shall not be cached.
Although disabling mod_expires does not necessarily prevent the resource from being cached. It simply prevents mod_expires from setting the Cache-Control (and Expires) headers. If you specifically want to disable caching then consider explicitly setting the relevant Cache-Control/Expires header(s) directly.
For example:
# Set env var when special "none-cache" folder requested:
SetEnvIf Request_URI "^/(specialfolder|anotherfolder)/" NOCACHE
# Set no-cache headers if NOCACHE env var is set
Header always set Cache-Control "no-store" env=NOCACHE
Header always set Expires "0" env=NOCACHE
In this example, you do not need to disable mod_expires since the Header directive will override mod_expires (since mod_headers is processed after mod_expires).

How do I create an exception for my htaccess setting that enables friendly URLs?

I have a LAMP server, but I do more website coding and design than Apache configuration. I hope my question will be clear because I don't understand the syntax of .htaccess files very well, and the code I have is mostly derived from tutorials on the web.
My website uses "friendly URLs", and to enable that function, I have the following code in my .htaccess file:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*) index.php
However, I've created a subdirectory with a whole different set of HTML under a subdirectory that I want to be able to access by going to a URL like this: http://www.example.com/subdirectory.
What is happening is that even if I go directly to http://www.example.com/subdirectory/index.html, Apache still redirects to index.php in the root.
Is there a way I can modify my .htaccess file so that my subdirectory will be exempted from the redirection?
UPDATE:
After some experimenting, I've determined that it is exactly this line that is causing the problem (which may be obvious to people more skilled than I am with .htaccess files):
RewriteRule ^(.*) index.php
If I comment that line out, everything in the subdirectory works fine (the rest of the site, of course, breaks). If I leave it in, I get the problem described above.
As suggested in an answer below, I tried changing the line to:
RewriteRule !^subdirectory index.php
But that didn't fix the problem. I also tried making a .htaccess file in the subdirectory with these contents:
RewriteEngine On
Unfortunately, that did nothing.
My limited understanding of .htaccess syntax says that the line I already have should leave the contents of my subdirectory alone, but it is acting far more aggressively than intended.
Is there a way I can diagnose and fix this line so that it does not act upon the contents of my subdirectory?
UPDATE 2:
After some experimentation, I found that the .htaccess file in the subdirectory is not being read. If I put gibberish text in it, I don't get any errors, which seems to indicate it's not even being accessed.
I looked for reasons why my .htaccess file might not be being read, and I found that in my .conf file for the site, I need to have this code:
AllowOverride All
I have that directive in there, but the .htaccess in the subdirectory is still not being read.
What to I do to ensure the .htaccess file in the subdirectory is being read?
Contents of my root .htaccess file:
# BEGIN Compress text files
<IfModule mod_deflate.c>
<FilesMatch "\.(css|js|x?html?|php)$">
SetOutputFilter DEFLATE
</FilesMatch>
</IfModule>
# END Compress text files
# BEGIN Expire headers
<IfModule mod_expires.c>
ExpiresActive On
ExpiresDefault "access plus 1 seconds"
ExpiresByType image/x-icon "access plus 2592000 seconds"
ExpiresByType image/jpeg "access plus 2592000 seconds"
ExpiresByType image/png "access plus 2592000 seconds"
ExpiresByType image/gif "access plus 2592000 seconds"
ExpiresByType application/x-shockwave-flash "access plus 2592000 seconds"
ExpiresByType text/css "access plus 604800 seconds"
ExpiresByType text/javascript "access plus 2592000 seconds"
ExpiresByType application/javascript "access plus 2592000 seconds"
ExpiresByType application/x-javascript "access plus 2592000 seconds"
ExpiresByType text/html "access plus 600 seconds"
ExpiresByType application/xhtml+xml "access plus 600 seconds"
</IfModule>
# END Expire headers
# BEGIN Cache-Control Headers
<IfModule mod_headers.c>
<FilesMatch "\.(ico|jpe?g|png|gif|swf)$">
Header set Cache-Control "max-age=2592000, public"
</FilesMatch>
<FilesMatch "\.(css)$">
Header set Cache-Control "max-age=604800, public"
</FilesMatch>
<FilesMatch "\.(js)$">
Header set Cache-Control "max-age=216000, private"
</FilesMatch>
<FilesMatch "\.(x?html?|php)$">
Header set Cache-Control "max-age=600, private, must-revalidate"
</FilesMatch>
</IfModule>
# END Cache-Control Headers
# BEGIN Turn ETags Off
<IfModule mod_headers.c>
Header unset ETag
</IfModule>
FileETag None
# END Turn ETags Off
# BEGIN Remove Last-Modified Header
<IfModule mod_headers.c>
Header unset Last-Modified
</IfModule>
# END Remove Last-Modified Header
# Make all requests pass through index.php to enable "friendly URLs"
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*) index.php
# Turn off 'magic quotes'
php_value magic_quotes_gpc off
# Do not allow Perl script hackers, as they are probably just feeding useless Adsense "directory" sites
RewriteBase /
RewriteCond %{HTTP_USER_AGENT} libwww-perl.*
RewriteRule .* - [F,L]
# Filter for most common exploits
RewriteCond %{HTTP_USER_AGENT} libwww-perl [OR]
RewriteCond %{QUERY_STRING} tool25 [OR]
RewriteCond %{QUERY_STRING} cmd.txt [OR]
RewriteCond %{QUERY_STRING} cmd.gif [OR]
RewriteCond %{QUERY_STRING} r57shell [OR]
RewriteCond %{QUERY_STRING} c99 [OR]
# deny most common except .php
<FilesMatch "\.(inc|tpl|h|ihtml|sql|ini|conf|class|bin|spd|theme|module)$">
deny from all
</FilesMatch>
# Disable .htaccess viewing from browser
<Files ~ "^\.ht">
Order allow,deny
Deny from all
Satisfy All
</Files>
This code is in my /etc/apche2/apache2.conf:
# Sets the default security model of the Apache2 HTTPD server. It does
# not allow access to the root filesystem outside of /usr/share and /var/www.
# The former is used by web applications packaged in Debian,
# the latter may be used for local directories served by the web server. If
# your system is serving content from a sub-directory in /srv you must allow
# access here, or in any related virtual host.
<Directory />
Options FollowSymLinks
AllowOverride None
Require all denied
</Directory>
<Directory /usr/share>
AllowOverride None
Require all granted
</Directory>
<Directory /var/www/>
Options Indexes FollowSymLinks
AllowOverride None
Require all granted
</Directory>
#<Directory /srv/>
# Options Indexes FollowSymLinks
# AllowOverride None
# Require all granted
#</Directory>
# AccessFileName: The name of the file to look for in each directory
# for additional configuration directives. See also the AllowOverride
# directive.
#
AccessFileName .htaccess
... and here is the contents of the conf file specific to the site in question:
<VirtualHost *:80>
ServerName www.local_example.com
ServerAlias local_example.com
ServerAdmin serveradmin#gmail.com
DocumentRoot /var/www/example.com
<Directory /var/www/example.com/>
Options Indexes FollowSymLinks MultiViews
# pcw AllowOverride None
AllowOverride All
Order allow,deny
allow from all
# This directive allows us to have apache2's default start page
# in /apache2-default/, but still have / go to the right place
# Commented out for Ubuntu
#RedirectMatch ^/$ /apache2-default/
</Directory>
ErrorLog /home/admin/Apache_Logs/local_example.com_error.log
# Possible values include: debug, info, notice, warn, error, crit,
# alert, emerg.
LogLevel debug
CustomLog /home/admin/Apache_Logs/local_example.com_access.log combined
ServerSignature On
</VirtualHost>
If /subdirectory is a physical directory on the filesystem and /subdirectory/index.html is an actual file then your current directives already include the necessary exception... the URL should not be rewritten if the requested URL maps to a physical directory or file.
However, to explicitly include an exception for the /subdirectory then you could do something like:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule !^subdirectory index.php
Only URLs that do not start /subdirectory will be processed. (Not that the directory-prefix is removed when matching the URL-path with the RewriteRule pattern, so it should be subdirectory here, not /subdirectory.)
Alternatively, you can create an additional .htaccess at /subdirectory/.htaccess and simply enable the rewrite engine:
# /subdirectory/.htaccess
RewriteEngine On
mod_rewrite directives are not inherited by default, so this should completely override the mod_rewrite directives in the parent .htaccess file. (Note that other directives from different modules might still be processed.)
UPDATE: I cannot see anything in your .htaccess file that would directly cause these symptoms, however, the following does need fixing/tidying...
# Do not allow Perl script hackers, as they are probably just feeding useless Adsense "directory" sites
RewriteBase /
RewriteCond %{HTTP_USER_AGENT} libwww-perl.*
RewriteRule .* - [F,L]
# Filter for most common exploits
RewriteCond %{HTTP_USER_AGENT} libwww-perl [OR]
RewriteCond %{QUERY_STRING} tool25 [OR]
RewriteCond %{QUERY_STRING} cmd.txt [OR]
RewriteCond %{QUERY_STRING} cmd.gif [OR]
RewriteCond %{QUERY_STRING} r57shell [OR]
RewriteCond %{QUERY_STRING} c99 [OR]
The 2nd code block above ("Filter for most common exploits") is incomplete and should not have a trailing OR flag (that would potentially block everyone!). These two code blocks also repeat the same code and should be combined. The RewriteBase directive is also redundant and can be removed. The above should be rewritten as:
# Do not allow Perl script hackers, as they are probably just feeding useless Adsense "directory" sites
# and Filter for most common exploits
RewriteCond %{HTTP_USER_AGENT} libwww-perl [OR]
RewriteCond %{QUERY_STRING} tool25 [OR]
RewriteCond %{QUERY_STRING} cmd.txt [OR]
RewriteCond %{QUERY_STRING} cmd.gif [OR]
RewriteCond %{QUERY_STRING} r57shell [OR]
RewriteCond %{QUERY_STRING} c99
RewriteRule .* - [F]
The F flag implies L, so the L flag is not required here.
UPDATE#2: Again, I cannot see anything in your VirtualHost container that would cause these problems (although, to be honest, I'm struggling to even imagine what could be causing this behaviour).
However, the following line is suspicious:
Options Indexes FollowSymLinks MultiViews
Why do you need MultiViews? Also, do you need Indexes? Most sites will disable these options as they can expose your file structure and result in unexpected behaviour (unless that behaviour is intentional of course). However, FollowSymLinks is required for mod_rewrite.
If these options are not required then try removing them:
Options FollowSymLinks
Incidentally, these options can also be disabled in .htaccess with the following directive (note that here the options are preceded with -):
Options -Indexes -MultiViews

Apache 2.2 conditional headers on response

I am trying to configure apache to send expires and cache-control headers on response but only if I receive some specific query string on the request.
For this I've tried configuring the server as follows:
RewriteCond %{QUERY_STRING} ^.*{whateverIWantInMyQueryString}.*$
RewriteRule ^(.*)$ - [env=CACHE_HEADERS:1]
<FilesMatch "\.(js|css|png|gif|GIF)$">
<IfDefine CACHE_HEADERS>
ExpiresActive On
ExpiresDefault "access plus 12 hours"
Header append Cache-Control "public"
</IfDefine>
</FilesMatch>
But this does not seem to work.
If I change the configuration as follows:
....
<IfDefine !CACHE_HEADERS>
....
It does work, so it seems IfDefine is not able to check that CACHE_HEADERS environment variable is being set.
I've also tried configuring things with Header directive and conditional based on the same environment variable using something like this:
RewriteCond %{QUERY_STRING} ^.*{whateverIWantInMyQueryString}.*$
RewriteRule ^(.*)$ - [env=CACHE_HEADERS:1]
<FilesMatch "\.(js|css|png|gif|GIF)$">
Header always set Cache-Control "max-age=3600, public" env=CACHE_HEADERS
Header always set Expires "Thu, 01 Jan 2015 00:00:00 GMT" env=CACHE_HEADERS
</FilesMatch>
.....
But this does not work either. So I am assuming this might be because of the environment variable that is being set by mod_rewrite, that for some reason is not being detected by neither IfDefine directive nor Header directive.
Does anyone know what may be causing this or why is this not working at all?
The reason why IfDefined is not working you can find here: Apache IfDefine conditionals in .htaccess
Basically it's that IfDefined only evaluates variables defined at command line not if you define them like you did or with SetEnv or SetEnvIf.
However as far as I know env= should work.

Prioritization of re-write rules in .htaccess and httpd.conf

I'm confused on which file's rewriting rules gets hierarchy in an apache (xampp) server.
Do .htaccess rules get priority over httpd.conf rules?
If you can add the same rules to each, what is the difference between these two files?
What got me wondering about this stuff is because I'd like to update my favicon every second (for learning purposes) but the code below in my .htaccess file isn't working. My .htaccess file is in the server root folder along with my index.php file which is recognized when I go to http://localhost.
<IfModule mod_rewrite.c>
RewriteEngine On
ExpiresActive On
ExpiresByType image/x-icon "access plus 1 seconds"
</IfModule>
Any thoughts?

Client side caching using Apache 2

I have a script that is consumming too much resource to provide data that could be retrieved only each minute. Is there a way to configure Apache 2 through an .htaccess file to specify headers telling to the client to keep the script result as is for one minute in its cache ?
I know this could be done through the script itself, but I would like to do this through the webserver's configuration.
mod_expires is the good solution.
If you are on a unix-like system :
a2enmod expires
apache2ctl restart
Then you will be able to define the expiration conditions for a given file, or define the cache policy according to mimetype through your .htaccess file.
<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType image/gif A3600
<Files scriptToCache.php>
ExpiresDefault A60
</Files>
</IfModule>
Here "A3600" means that the file expires 3600 seconds after access.
More information here : http://httpd.apache.org/docs/2.0/mod/mod_expires.html
Short answer is no - you need to return the caching headers from the script.
<IfModule mod_expires.c>
<FilesMatch "\.(jpe?g|png|gif|js|css)$">
ExpiresActive On
ExpiresDefault "access plus 1 year"
</FilesMatch>
</IfModule>