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. Commented Nov 20, 2020 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. Commented Nov 22, 2020 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. Commented Nov 23, 2020 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? Commented Nov 23, 2020 at 19:58
  • @mateleco Done, post edited. Commented Nov 24, 2020 at 12:40

2 Answers 2

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. Commented Nov 25, 2020 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/  

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.