1

I'm trying to redirect https://www.example.com to https://example.com.

I'm running Ubuntu server 2020 lts with Apache 2, PHP 7 and Let's Encrypt SSL.

My file structure:

logs/
private/
public/
  └ index.php
.htaccess
index.php

My Apache2 config file (/etc/apache2/sites-available/example.com-le-ssl.conf):

<IfModule mod_ssl.c>
    <VirtualHost *:443>
    
        <Directory /var/www/example.com/>
            Options Indexes FollowSymLinks
            AllowOverride All
            Require all granted
            Options -Indexes
            Options -Includes
            Options -ExecCGI
        </Directory>  
    
        ServerAdmin webmaster@localhost
        ServerName example.com
        ServerAlias www.example.com
        DocumentRoot /var/www/example.com
        ErrorLog ${APACHE_LOG_DIR}/error.log
        CustomLog ${APACHE_LOG_DIR}/access.log combined
    
    Include /etc/letsencrypt/options-ssl-apache.conf
    SSLCertificateFile /etc/letsencrypt/live/example.com-0001/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/example.com-0001/privkey.pem
    </VirtualHost>
</IfModule>

(/etc/apache2/sites-available/example.conf):

<VirtualHost *:80>

    <Directory /var/www/example.com/>
        Options Indexes FollowSymLinks
        AllowOverride All
        Require all granted
        Options -Indexes
        Options -Includes
        Options -ExecCGI
    </Directory>
    
    ServerAdmin webmaster@localhost
    ServerName example.com
    ServerAlias www.example.com
    DocumentRoot /var/www/example.com
    ErrorLog ${APACHE_LOG_DIR}/error.log
    CustomLog ${APACHE_LOG_DIR}/access.log combined
RewriteEngine on
RewriteCond %{SERVER_NAME} =www.example.com [OR]
RewriteCond %{SERVER_NAME} =example.com
RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent]
</VirtualHost>

My .htaccess file:

RewriteEngine On

# First rule
RewriteBase /
RewriteCond %{HTTP_HOST} ^www\.(.*)$ [NC]
RewriteRule ^(.*)$ https://%1/$1 [R=301,L]

# Second rule
RewriteCond %{THE_REQUEST} ^GET\ /public/ [NC]
RewriteRule ^public/(.*)$ $1 [L,R=301,NE]
RewriteRule ^((?!public/).*)$ public/$1 [L,NC]

cURL output:

curl -I http://www.example.com
HTTP/1.1 301 Moved Permanently
Date: Thu, 01 Apr 2021 13:42:14 GMT
Server: Apache
Location: https://www.example.com/
Content-Type: text/html; charset=iso-8859-1

I don't understand why the first rule doesn't redirect as expected.

The second rule works, it hides public. I'm in the public directory but public is invisible in https://example.com/.

22
  • So your .htaccess file and index.php aren't in the public directory? I'd expect the public directory to be the document root where those files reside. – Stephen Ostermiller Apr 1 at 15:18
  • 1
    You don't have a valid security cert?! That's why it's not continuing (when using the -L flag). – MrWhite Apr 2 at 9:51
  • 1
    I'have for cleoni.com, for www I want to redirect – Mantykora 7 Apr 2 at 9:54
  • 1
    You NEED a valid cert otherwise you can't redirect - a secure connection needs to be made with the server before it can redirect. You could change the redirect in vHost:80 to redirect directly to HTTPS + the canonical hostname, ie. example.com (although this won't work for HSTS), but this will still be a problem when a user tries to request https://www.example.com directly. – MrWhite Apr 2 at 9:59
  • 1
    @MrWhite YES:))) ITS WORK!!!!!!!!! Thank you very much!!!!! If you want, write an answer and I will approve it – Mantykora 7 Apr 2 at 10:46
2

There's nothing actually "wrong" with the config/directives you've posted, but it seems you don't have a valid SSL cert that covers the www subdomain so you can't redirect from https://www.example.com/ without the user accepting the invalid cert (this is the "error") - which they should never do.

When requesting HTTPS, the browser establishes a secure connection with the server before any server-side scripts/redirects are even processed. If the security cert is invalid (as in this case), the user sees a warning in the browser and are unable to continue (unless the user jumps through hoops and accepts the invalid cert - which they should never do, as already mentioned). If there is no SSL cert at all then the server simply fails to connect.

So, there's nothing actually "wrong" with the config you've posted. Providing you have a valid cert installed for the www subdomain then a request for http://www.example.com/ would be redirected to https://www.example.com/ by the mod_rewrite directive in the <VirtualHost *:80> container and then your directives in .htaccess would redirect from https://www.example.com/ to https://example.com/ (canonicalising the hostname). Yes, that's two redirects, but there's nothing wrong with two redirects here. And two redirects would actually be a requirement if implementing HSTS.

When testing multiple redirects (a redirect "chain") with CURL then you need to use the -L flag, otherwise it does not follow the first redirect. For example:

curl -I -L http://www.example.com/

But in this case, without a valid cert installed for the www subdomain you get a warning back from CURL and further requests are stopped:

curl: (51) SSL: no alternative certificate subject name matches target host name 'www.example.com'


However, you could optimise this a bit (if HSTS is not a concern) and redirect directly to the canonical hostname (ie. example.com) from the <VirtualHost *:80> container. You can also do this with a simple mod_alias Redirect. You don't need mod_rewrite for this.

For example, instead of the following in <VirtualHost *:80>:

RewriteEngine on
RewriteCond %{SERVER_NAME} =www.example.com [OR]
RewriteCond %{SERVER_NAME} =example.com
RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent]

you replace this with:

Redirect 301 / https://example.com/

There's no need to check the requested host (ie. SERVER_NAME) anyway in the vHost container, since it can't be anything other than the two hostnames you've defined with the ServerName and ServerAlias directives. (Assuming you have a default vHost:80 defined earlier that catches all "invalid" requests? Either way, they will all be redirected to your canonical hostname anyway.)

With this in place, any request for HTTP (ie. http://example.com/ or http://www.example.com/) will be redirected directly to https://example.com in a single redirect (the redirect in .htaccess is not required in this instance). This naturally circumvents the security cert issue for any request to HTTP only.

HOWEVER, any direct request the user makes to https://www.example.com/ will still be subject to the SSL cert warning until a valid cert is installed.


Aside: Due to rewriting all requests to the /public subdirectory (which contains index.php - the "front-controller" I assume), the additional /index.php in the document root is entirely superfluous.

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.