apache.htaccessmod-rewritewebservermod-expires

How to make this .htaccess server-friendly and apply to .php?


I don't know anything about .htaccess-files except the very basics.
I copied and pasted this code from different sources in the internet and so far, it works. The code (a) defines the 404-errorpage, (b) defines how long certain files should be "saved", (c) removes the .html-extension from all URLs and (d) adds a trailing slash to them. I just want to know two things:

  1. How can I apply this code not just for .html-files but for .php-files, too? I suspect I have to change line 6, but I'm not sure.
  2. Are there any opinions from more experienced users on how server-friendly this code is? As I said, I just copied the code, but I want to know if there are any unnecessary redirects, possible SEO-penalties or something like that.

This is the code I'm using in the .htaccess-file:

ErrorDocument 404 /404.html
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^([^/]+)/$ $1.html
RewriteRule ^([^/]+)/([^/]+)/$ /$1/$2.html
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} !(\.[a-zA-Z0-9]{1,5}|/)$
RewriteRule ^(.*)$ /$1/ [R=301,L]
<IfModule mod_expires.c>
  ExpiresActive on
  ExpiresByType text/html "access plus 0 seconds"
  ExpiresByType text/css "access plus 1 week"
  ExpiresByType application/javascript "access plus 1 month"
  ExpiresByType application/x-javascript "access plus 1 month"
  ExpiresByType audio/ogg "access plus 1 month"
  ExpiresByType image/gif "access plus 1 month"
  ExpiresByType image/jpeg "access plus 1 month"
  ExpiresByType image/png "access plus 1 month"
  ExpiresByType video/mp4 "access plus 1 month"
  ExpiresByType video/ogg "access plus 1 month"
  ExpiresByType video/webm "access plus 1 month"
  ExpiresByType image/x-icon "access plus 1 month"
</IfModule>

Thank you for your help!


Solution

  • RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule ^([^/]+)/$ $1.html
    RewriteRule ^([^/]+)/([^/]+)/$ /$1/$2.html
    

    You can't simply "change line 6" to handle .php files as well. Since you introduce an ambiguity. For example, should a request for /foo map to .html or .php? You need to prioritise one or the other and check for the presence of the corresponding .php or .html file.

    Incidentally, RewriteCond directives only apply to the first RewriteRule directive that follows, so the 2nd RewriteRule directive above is being processed unconditionally. (Although you don't need the "file" check here, since filenames don't end in a slash.)

    Also, the handling of one or two path segments (the first and second RewriteRule directives respectively) can be combined into a single rule.

    Try the following instead to handle both .html and .php files. I'll assume that .php files take priority. So, if you request /foo and both foo.php and foo.html exist then foo.php will be served (foo.html would only be accessible if you request it directly).

    # Rewrite request to ".php" if it exists
    RewriteCond %{DOCUMENT_ROOT}/$1.php -f
    RewriteRule ^((/?[^/]+){1,2})/$ $1.php [L]
    
    # Otherwise, rewrite request to ".html" if it exists
    RewriteCond %{DOCUMENT_ROOT}/$1.html -f
    RewriteRule ^((/?[^/]+){1,2})/$ $1.html [L]
    

    Note also the inclusion of the L (last) flag.

    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_URI} !(\.[a-zA-Z0-9]{1,5}|/)$
    RewriteRule ^(.*)$ /$1/ [R=301,L]
    

    These directives could be simplified and optimised by moving the test in the 3rd condition to the RewriteRule pattern and avoid unnecessary filesystem checks.

    For example:

    # Append trailing slash if omitted
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule !(^$|\.[a-zA-Z0-9]{1,5}|/)$ %{REQUEST_URI}/ [R=301,L]
    

    Also, these rules are arguably in the wrong order (although it probably won't make a difference in this instance). As a general rule, the external redirect (above) should go before the internal rewrites that append the .php or .html extensions, to avoid internal rewrites being inadvertently redirected.

    So, in summary, the first part of the file becomes:

    ErrorDocument 404 /404.html
    
    RewriteEngine On
    
    # Append trailing slash if omitted
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule !(^$|\.[a-zA-Z0-9]{1,5}|/)$ %{REQUEST_URI}/ [R=301,L]
    
    # Rewrite request to ".php" if it exists
    RewriteCond %{DOCUMENT_ROOT}/$1.php -f
    RewriteRule ^((/?[^/]+){1,2})/$ $1.php [L]
    
    # Otherwise, rewrite request to ".html" if it exists
    RewriteCond %{DOCUMENT_ROOT}/$1.html -f
    RewriteRule ^((/?[^/]+){1,2})/$ $1.html [L]
    

    Then followed by the mod_expires directives...

    ExpiresByType application/javascript "access plus 1 month"
    ExpiresByType application/x-javascript "access plus 1 month"
    

    Only one of these directives is required. Your server will either be serving JS files with a application/javascript mime-type OR application/x-javascript. It cannot (should not) be both. Most probably the former.

    ...how server-friendly this code is? As I said, I just copied the code, but I want to know if there are any unnecessary redirects, possible SEO-penalties or something like that.

    Not sure what you mean by "server-friendly". But otherwise, whether this works as intended, without unnecessary redirects is really dependent on the URL structure throughout your site, so only you can really answer this... test test test.

    But otherwise, it looks OK after these updates.