1

This is the configuration of my Apache 2.4.37 webserver on Centos 8.

File /etc/httpd/conf.d/mysite.conf:

<VirtualHost *:80>

    ServerName mysite.com
    DocumentRoot "/var/www/html"

    RewriteEngine on

    RewriteCond %{SERVER_NAME} =mysite.com
    RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent]

</VirtualHost>

File /etc/httpd/conf.d/mysite-ssl.conf:

<IfModule mod_ssl.c>
<VirtualHost *:443>
    ServerName mysite.com
    DocumentRoot "/var/www/html"

    Include /etc/letsencrypt/options-ssl-apache.conf
    SSLCertificateFile /etc/letsencrypt/live/mysite.com/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/mysite.com/privkey.pem

    ErrorDocument 403 /error403.html
    ErrorDocument 404 /error404.html
    ErrorDocument 405 /error405.html
    ErrorDocument 500 /error500.html

    RewriteEngine on

    # First rule block
    RewriteRule ^/$ /index.php [R,L]

    # Second rule block    
    RewriteCond %{THE_REQUEST} ^[A-Z]{3,9}\ /(([^/]+/)*([^/.]+))\.php[\ ?]
    RewriteRule \.php$ /%1/ [R=301,NC,L]
    RewriteRule ^(.*)/$ /$1.php [NC,L]

    Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains"

    TraceEnable off
</VirtualHost>
</IfModule>

The second rule block is taken from here and rewrites all URLs https://mysite.com/anypage.php to https://mysite.com/anypage/, hiding PHP file extensions and making permalinks nicer to see.

I've added the first rule block after noticing that the solution suggested in the link had a bug -- that is, URL https://mysite.com/ returned a File not Found. Now it works.

However, a minor annoyance is that https://mysite.com/ redirects to https://mysite.com/index/ (since it loads the file index.php).

My question: How can this configuration be changed so that the URL https://mysite.com/ stays the same?

6
  • Place RewriteRule ' ^/$ /index.php [R,L] ' to the end. – Reda Salih Nov 20 '20 at 19:24
  • 1
    @RedaSalih That accomplishes nothing and rather restores the File not Found problem for the website root. Even when removing the L[ast] flag so rule checking continues. – dr_ Nov 22 '20 at 19:31
  • 1
    @RedaSalih The [L] flag will only prevent the underlying rules from being applied in the current iteration. However, Apache iterates over and over again from the beginning until the URL stops changing. In this case (and in general), it takes more than one iteration, so your solution will not work. – mateleco Nov 23 '20 at 0:36
  • @dr_ I have tested your code in my localhost and works fine. http://localhost/ stays the same, and http://localhost/foo.php redirects to http://localhost/foo/. Maybe share the full code? – mateleco Nov 23 '20 at 19:58
  • @mateleco Done, post edited. – dr_ Nov 24 '20 at 12:40
1
+50

I've added the first rule block after noticing that the solution suggested in the link had a bug -- that is, URL https://mysite.com/ returned a File not Found. Now it works.

I think it is not really a bug. The code you extracted from here is meant to be used in a .htaccess file, not in a VirtualHost directive. It turns out that RewriteRule does not work exactly the same in a .htaccess file as in a VirtualHost directive, and that is what is causing some problems.

Let's see the original code:

RewriteEngine On
RewriteCond %{THE_REQUEST} ^[A-Z]{3,9}\ /(([^/]+/)*([^/.]+))\.php[\ ?]
RewriteRule \.php$ /%1/ [R=301,NC,L]
RewriteRule ^(.*)/$ /$1.php [NC,L]

In a VirtualHost directive (unlike in .htaccess), the leading slash of the path is also matched against the regular expression, thus, when you send a request to http://mysite.com/, the rule

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

is matching / with the regular expression ^(.*)/$. This means that the backreference $1 will be equal to an empty string, and the resulting path will be /.php which does not exist in your server.

The easy solution is to put the original code in a .htaccess file in the DocumentRoot of your server (i.e., the directory that has all your public files).

However, if you want to keep the RewriteRules in a VirtualHost directive (not recommended), you can modify the original code a little bit by prepending a slash:

RewriteEngine On
RewriteCond %{THE_REQUEST} ^[A-Z]{3,9}\ /(([^/]+/)*([^/.]+))\.php[\ ?]
RewriteRule \.php$ /%1/ [R=301,NC,L]
RewriteRule ^/(.*)/$ /$1.php [NC,L]

Now when sending a request to http://mysite.com/, Apache will try to match / against ^/(.*)/$ with no success, so the URL will remain unchanged. On the other hand, a request to http://mysite.com/foo/ will match /foo/ against ^/(.*)/$, mapping it to /foo.php which is the desired result.

Also, bear in mind that when you don't specify the full destination URL in RewriteRule, Apache assumes it to be from your server's local file system (not the DocumentRoot):

A rewrite target that does not begin with http:// or another protocol designator is assumed to be a file system path.

1
  • Ah, you're right. Even if this does not solve my original problem (the behavior is identical regardless of the fact that the URL rewriting code is placed in the HTTPD conf or in an .htaccess), which I solved in a stupidly simple way, I am awarding you the bounty for the work done. Thanks. – dr_ Nov 25 '20 at 12:14
1

I've solved my issue by changing all the internal links to the home page from

https://mysite.com/index.php 

to

https://mysite.com/  

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.