2

This is a .php file extension removal snippet, for displaying links online.

RewriteCond %{THE_REQUEST} ^[A-Z]{3,}\s([^.]+)\.php [NC]    
RewriteRule ^ %1 [R,L]  

This is where I make an exception to that .php file extension removal

RewriteCond %{REQUEST_FILENAME} !^/PhP/?$  
RewriteCond %{REQUEST_FILENAME} !^/forms/?$    
RewriteCond %{REQUEST_URI} !^/PhP/?$    
RewriteCond %{REQUEST_URI} !^/forms/?$    

followed by the removal snippet

RewriteCond %{REQUEST_FILENAME} !-d    
RewriteCond %{REQUEST_FILENAME}.php -f    
RewriteRule ^(.*?)/?$ $1.php [L]    

and here is the server environment:

  • cPanel Version 78.0 (build 44)
  • Apache Version 2.4.39
  • PHP Version 5.4.45
  • MySQL Version 5.6.41-84.1
  • Architecture x86_64
  • Operating System linux

in this snippet, I want to exclude two directories: PhP & forms, as the snippet is apparently preventing me from database access, as all connect & config files lie in those directories- however, the above example is ineffective. the four RewriteCond directives in the middle section above, are attempting to make the exceptions, followed by (last 3 lines) the original snippet code. What am I doing wrong?

  • Do you have any other mod_rewrite directives in your .htaccess file? – MrWhite Jan 10 at 17:07
1
RewriteCond %{REQUEST_FILENAME} !^/PhP/?$  
RewriteCond %{REQUEST_FILENAME} !^/forms/?$    
RewriteCond %{REQUEST_URI} !^/PhP/?$    
RewriteCond %{REQUEST_URI} !^/forms/?$

You don't need to check against both REQUEST_FILENAME and REQUEST_URI - you're just repeating the same check. It's easier to use REQUEST_URI (the URL-path) in this instance.

The REQUEST_FILENAME server variable contains the absolute filesystem path (not the URL-path) that the request maps to, so will never start with /PhP/ or /forms/ anyway - so the these two conditions will always be successful.

However, the second two conditions that check against REQUEST_URI are only checking the directory itself - because of the trailing end-of-string anchor ($) on the regex - this is not excluding the "connect & config files*1 within those directories". (Presumably /PhP.php and /forms.php don't exist anyway, so your last condition is already excluding these requests anyway.)

(*1 Although you wouldn't expect "connect & config" files to require being accessible over HTTP anyway??)

You also need to include these same "exceptions" on the first rule that redirects requests for .php to remove the extension.

Try the following instead:

# 1. Remove ".php" extension (external redirect)
RewriteCond %{THE_REQUEST} ^[A-Z]{3,}\s([^.]+)\.php [NC]    
RewriteRule !^(PhP|forms) %1 [R,L]  

# 2. Internal rewrite to append the ".php" extension
RewriteCond %{REQUEST_URI} !^/(PhP|forms) 
RewriteCond %{REQUEST_FILENAME} !-d    
RewriteCond %{REQUEST_FILENAME}.php -f    
RewriteRule ^(.*?)/?$ $1.php [L]

Note that I've combined the two (short) directories into a single condition using regex alternation. If you want to exclude many directories then it may be easier to read/maintain if you have multiple conditions.

On the first "redirect", we can include the exception directly in the RewriteRule pattern, since this is not being used in the substitution string (unlike the later rewrite). This is more efficient, as the RewriteRule pattern is processed first (before the preceding conditions). Note that the URL-path matched by the RewriteRule pattern does not start with a slash (unlike the value of the REQUEST_URI server variable, which contains the full URL-path, including the slash prefix).

The CondPattern !^/(PhP|forms) simply checks that the requested URL-path does not start with /PhP or /forms - we don't need to include the trailing slash here (unless you also had files named /PhP.php or /forms.php in the document root - but I assume not, since the later condition that checks !-d would already block these requests from being rewritten).


Aside:

RewriteRule ^(.*?)/?$ $1.php [L]

You are currently allowing an optional trailing slash on your URLs. I would settle on one or the other, not both (from an SEO perspective). You are presumably consistent in your URL structure and only linking to one or the other (and including a rel="canonical" in your pages), so really that is the only version you need to check for in your directives.

The URL with and without the trailing slash are two different URLs - yet you are serving the same content for both. Strictly speaking this is potentially duplicate content.

UPDATE: Your initial redirect (that removes the .php extension from old URLs) actually redirects to the URL without a trailing slash - so I would assume this is the desired option.

what would be the expression which does NOT include the slash?

Ordinarily you would just assume there is no slash on the end of the URL (the rewrite will fail*2 if there is), so the directive is simplified to:

RewriteRule (.*) $1.php [L]

If you specifically wanted to avoid matching any URL that includes a trailing slash (so nothing happens, ie. a 404 will default) then you would do something like the following instead:

RewriteRule (.*[^/])$ $1.php [L]

*2 Unfortunately, if you attempt to rewrite a URL that does end with a slash, this will currently fail with a 500 error since it will cause an internal rewrite loop due to the (arguably incorrect) use of REQUEST_FILENAME in the preceding condition. A 404 would be more desirable. This can be resolved by doing the following (note the last condition) instead:

# 2. Internal rewrite to append the ".php" extension
RewriteCond %{REQUEST_URI} !^/(PhP|forms) 
RewriteCond %{REQUEST_FILENAME} !-d    
RewriteCond %{DOCUMENT_ROOT}/$1.php -f    
RewriteRule (.*) $1.php [L]

See my answer to the following ServerFault question for a detailed explanation of this:

  • 1
    That worked! very appreciated, @MrWhite - 2 more comments according to your answer: – imsimpl Jan 12 at 10:17
  • 1. if RewriteRule ^(.*?)/?$ $1.php [L] is the undesired optional trailing slash, what would be the expression which does NOT include the slash?.....as i am not an expert in this arena - cheers! im – imsimpl Jan 12 at 10:18
  • 2. as far as being online, my forms p1bizform.php and p25bizform.php, actually get loaded manually , by me, while online. this is for loading up a directory listing like yellow pages. hitting submit results in redirect to a success page, and i do this because it is possible to enter - or edit- records on my cellphone, if i wish. now these forms are back online and i return to loading in more directory listings – imsimpl Jan 12 at 10:25
  • 1
    I've updated my answer with the required changes. – MrWhite Jan 12 at 16:15
  • 3. gratefully incorporated @MrWhite – imsimpl Jan 14 at 10:51

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.