10

I am trying to update/create a .htaccess file for my apache2 server so that my webpages can be accessed without file extensions (e.g. www.example.com/whatis.php can now be accessed as www.example.com/whatis/). I have tried following instructions on editing .htaccess and now created a .htaccess here:

/etc/apache2/sites-available/.htaccess

I tried reading up on .htaccess files here: http://httpd.apache.org/docs/2.2/howto/htaccess.html http://wiki.apache.org/httpd/Htaccess

But I didn't find the docs helpful. My understanding is I have to put a file named ".htaccess" at each level/directory where I want to give the server specific instructions. So since I want the server to cut off the .php extension of all files, I created the .htaccess file at the location above and put this inside it:

RewriteEngine on
RewriteRule ^([^/\.]+)/?$ $1.php [L]

However even after restarting my apache2 server, I still cannot shorten the pathname - if I try to load the pathname minus the .php, the page is not found. I am guessing the problem is with my .htaccess file rather than the code.

So long story short, where should I put my .htaccess file and how do these work in general? If there's a recommended resource out there, I'm interested. Thank you.

4
  • Is /etc/apache2/sites-available your document root? Or is something like /etc/apache2/sites-available/site1 your document root? Can you get anything to work in your .htaccess file? The directives you have so far might not be complete... what URLs are you trying to rewrite? You can more easily test rewrites by temporarily changing them to "temporary" (302) redirects ie. use the [R] flag on the RewriteRule.
    – MrWhite
    Mar 19, 2015 at 7:45
  • @w3d I put random junk into my .htaccess file, and then I get an internal server error, so my .htaccess is being used. It must be my file ending rewrite that is incorrect. I will look into this.
    – Fawn
    Mar 19, 2015 at 20:11
  • @w3d I tried my code above and I also tried this: RewriteEngine On RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^([^\.]+)$ $1.php [NC,L] from here: alexcican.com/post/…. This seems like quite a standard thing I want to do, and yet the code is not working for me even though I verified that the htaccess does give garbage if I put garbage in it. Any advice? What else should I check?
    – Fawn
    Mar 19, 2015 at 20:16
  • Does mod_rewrite actually do something? ie. Do a simple (302) redirect - does it redirect? A similar question was asked recently... webmasters.stackexchange.com/questions/77947/… (presumably you aren't linking to the .php URLs - unlike the linked question!?)
    – MrWhite
    Mar 20, 2015 at 10:17

3 Answers 3

6

If you want a folder/directory to have its own htaccess rules, you can put a .htaccess file in that directory and that file will supersede the .htaccess file that's in your root directory.

An .htaccess file in each subfolder is used for each URL, and the .htaccess file closest to the URL takes priority when two htaccess files instruct different things.

Example:

For example.com/dir1/dir2/page.html, the .htaccess files in example.com/, dir/ and dir2/ all are used, and the .htaccess file in dir2 overwrites any conflicts in the .htaccess files of dir1 and dir2.

If there isn't a .htaccess file in a specific directory, that's ok. The other available .htaccess file(s) will be used.

For example, this allows the .htaccess file in your root directory to block IP addresses for all URLs on your site. And you can create custom rules in specific subdirectories that you don't want to have for other subdirectories on your site.


Here is an example for displaying example.com/page instead of example.com/page.php :

#remove php file extension
#https://example.com/file.php will become https://example.com/file
RewriteEngine on 
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}\.php -f
RewriteRule ^(.*)$ $1.php [NC,L]

example.com/page instead of example.com/page.html

#remove html file extension 
#https://example.com/file.html will become https://example.com/file
RewriteEngine on 
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}\.html -f
RewriteRule ^(.*)$ $1.html [NC,L]

After implementing the above htaccess code, if there are any directories on the site that still have the .php and .html extensions, also try implementing the above htaccess code in the .htaccess file of the directory that this is occurring. The .htaccess file in that directory should take precedent over .htaccess files closer to your root.


Also worth noting is that .htaccess files work chronologically, meaning that lines of code later in a .htaccess file supersede lines of code that are written earlier. So if you are having any issues removing .php and .html extensions, it is worth trying to put that code at the very bottom of your .htaccess file in case there is any other code in there that is conflicting with it.

Users who are having trouble removing .php and .html extensions on Wordpress should try to put the above .htaccess code after the # BEGIN WordPress # END WordPress lines.

1
  • If there's anything incorrect with this post please feel to tell me about it in the comments or message me so that I can edit it, as I slapped the post together. I'll delete this comment later on.
    – Michael d
    Mar 19, 2018 at 23:49
2
+250

Within your <VirtualHost ...> definition you have a DocumentRoot.

For example, say you are creating a website example.com and place that website under:

/var/www/example.com/public_html/

Your <VirtualHost ...> would look something like:

<VirtualHost *:80>
    ServerAdmin webmaster@example.com
    DocumentRoot /var/www/example.com/public_html/
    ServerName example.com

    ... # other descriptions
</VirtualHost>

First, you can have a .htaccess under /var/www/example.com/public_html/. This is called your root folder.

Any sub-folder you have under that root folder can also include its own .htaccess. A sub-folder .htaccess has priority over the root or any parent folder.

For example, Drupal has a .htaccess in its files folder to prevent anyone from executing PHP code in that folder. That files folder is where you can upload files from the Drupal front end. In other words, you could end up uploading a .php and then try to execute it. With that .htaccess there, they prevent the possibility of such execution. The command they use is:

php_flag engine off

The /etc/apache2/sites-available/*.conf configuration files, on the other hand, include commands just like what you'd put in the .htaccess, so there is no need for any .htaccess under /etc/apache2/....

The difference is that you use the <Directory ...> directive to tweak the flags there. For that Drupal example, you would use:

<Directory "/var/www/example.com/public_html/sites/default/files">
    php_flag engine off
</Directory>

This achieve exactly the same feat as the .htaccess file and in most cases will be a lot faster.

The .htaccess is great on a system like GoDaddy or Bluehost when you get shared hosting. That is useful because you do not have direct access to the /etc/apache2 files. Otherwise, turning off that feature will make things go faster because Apache won't have to check whether there is a .htaccess file. For the files example above, this means checking the following directories:

/var/www/example.com/public_html/sites/default/files
/var/www/example.com/public_html/sites/default
/var/www/example.com/public_html/sites
/var/www/example.com/public_html

The first .htaccess is used so in that specific case, it stops at the first directory. If you have many sub-directories and most don't have a .htaccess, that's going to probe all of those over and over again. So not using those files (and turning off the feature in your /etc/apache2/... settings) is going to save your server quite a few cycles.

Finally, to answer your question about how you usually execute a .php file when you want the user to just enter a normal path without the .php, you use an index.php (in most cases, you can really name that file anything you want) and from there you execute the necessary code using various include/require and so on. If your code is straight forward, a simple require will be enough.

The Apache code would look something like this:

<Directory "/var/www/example.com/public_html">
    DirectoryIndex index.php

    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule ^(.*)$ index.php?q=$1 [L,QSA]
</Directory>

This says to execute index.php putting the path in the q query string. (This is the Drupal way, you don't actually need the query string.) Personally, I used the following:

<Directory "/var/www/example.com/public_html">
    DirectoryIndex index.php

    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule ^(.*)$ /index.php [last,passthrough,qsappend]
</Directory>

But it may not work on your system depending on the other settings in your Apache2 server.

In your index.php you will get a path as the REQUEST_URI parameter:

$pos = strpos($_SERVER["REQUEST_URI"], '?');
if($pos === FALSE)
{
    $path = $_SERVER["REQUEST_URI"];
}
else
{
    // remove the query string if present
    //
    $path = substr($_SERVER["REQUEST_URI"], 0, $pos);
}

Now you have a path and you can run the corresponding code:

require $path . ".php";

However, I strongly recommend that you verify the $path content as it can be strongly tainted. First if the file does not exist you want to return a 404:

$php = $path . ".php";
if(!file_exists($php))
{
    http_response_code(404);
    echo "<h1>Page Not Found</h1><p>Sorry Could Not Find Your Page...</p>";
}
switch($path)
{
case 'this-path-is-allowed':
case 'and/it/works/with/sub-paths':
    require $php;
    break;

default:
    // emit another error, maybe a 403 or 500?
    http_response_code(403);
    echo "<h1>Page Not Accessible</h1><p>Sorry Not Authorized Here.</p>";
    break;

}

As we can see, one can avoid the .php that way and also better control what gets run and what doesn't. That way you can have many .php files, but only allow a very few to be run as expected (although putting those that should never be directly accessible by Apache outside of the root directory is way smarter, just not always doable depending on your host or the CMS you're using.)

Another way, although I never tried that, would be to test whether the .php exists and if so rewrite the path:

RewriteCond %{REQUEST_FILENAME}.php !-f
RewriteRule ^(.*)$ /$1.php [last,passthrough,qsappend]

Obviously, any .php file in your directory will then be executable from the front end. Something to keep in mind. When I write my own PHP code, though, I only put public files in the root directory and below so it's not a problem for me. This is why I have a public_html sub-directory. My other PHP files will generally go under a separate php directory:

/var/www/example.com/php           -- server side only PHP files
/var/www/example.com/public_html   -- public PHP, image, CSS, JS... files

Anything inside /var/www/example.com/php can be included from the PHP file found under /var/www/example.com/public_html, however, the client has not access to those files. Much more secure than WordPress, Drupal, etc. (and yet they rule the world?!)

Another important aspect of that. If you let the user access the file either way, with the .php and without (which some of my code above may do) then you should also put a canonical meta tag in your pages because otherwise Google is going to see duplication and frown about it.

1

You should place the .htacess file at /var/www/html. If you don't have a html folder place it in www directory.

Before that you need to change some other configurations. Since you need to enable url rewriting you should first enable it. This can be done by the following command.

sudo a2enmod rewrite

Then you should change the Apache default configuration file. Open this file by entering

sudo nano /etc/apache2/sites-available/default.

Once inside that file, find the following section, and change the line that says AllowOverride from None to All. The section should now look like this:

<Directory /var/www/>
                Options Indexes FollowSymLinks MultiViews
                AllowOverride All
                Order allow,deny
                allow from all
 </Directory>

After you have completed this, restart the apache server by the following command

sudo service apache2 restart

This should do the job.

3
  • thank you very much for these detailed instructions. I tried to follow them, but they don't seem to have worked. In particular, this might be because this file /etc/apache2/sites-available/default was empty. I added the code you specified, but since there was nothing else in there I wonder if I might have gone wrong somehow? This is what is available in my sites-available folder: 000-default.conf default default-ssl.conf but I created the 'default' file, it wasn't there before. Is something wrong with creating it?
    – Fawn
    Mar 18, 2015 at 22:38
  • @Fawn Not every Apache install is the same. The file you created will not do you any good. it is possible that 000-default.conf is your configuration file. It is likely that you will not have to change anything with this file. Just make sure the AllowOverride All is in there like Vishnu says. As well, not all Apache "web space" directories are the same. You want to put your .htaccess file where the <Directory > directive says the web space is (just like in the example above).
    – closetnoc
    Mar 18, 2015 at 23:59
  • I found the config file for apache at /etc/apache2/apache2.conf. I'm suffering from this problem at the moment and still cant fix it.Dont have any error, just keeps displaying the .php reguardless of changing the apache config Mar 19, 2018 at 11:05

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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