Welcome to Abdul Malik Ikhsan's Blog

Use single SSL Certificate for www and non-www with Let’s Encrypt

Posted in Teknologi by samsonasik on January 20, 2023

If you have your own server or vps which you need to setup the SSL for you domain yourself, you can use single key for both www and non-www, just with single command. There is Certbot tool for that!

Some preparation to have:

  • both www and non-www already accessible, eg: www.yourdomain.com and yourdomain.com
  • have access to the server from SSH and sudo access

This post is assume you’re using Apache for PHP application with Ubuntu Operarating system.

Here the steps you can do:

1 Install the CertBot with instruction at https://certbot.eff.org/instructions?ws=apache&os=ubuntufocal

You can follow the instruction for other web server and operating system by choose which web server and operating system you use.

2 Run the CertBot command to generate SSL key for both www and non-www:

echo "2" | /usr/bin/certbot certonly --apache \
       --domains yourdomain.com,www.yourdomain.com \
       && /usr/bin/systemctl reload apache2 && /usr/bin/systemctl restart apache2

The option “2” above is used to Renew and Replace instead of keep existing certificate, then provide multiple domains after --domains to provide multiple domains separated by ,:

The SSL certificate will be generated at the following locations:

  • /etc/letsencrypt/live/yourdomain.com/fullchain.pem
  • /etc/letsencrypt/live/yourdomain.com/privkey.pem

or based on your output information.

3 If your site use default domain, you may need to lookup the following paths:

  • /etc/apache2/sites-available/000-default.conf
  • /etc/apache2/sites-available/default-ssl.conf

If you have different domain then root domain, create new one for them, eg: yourdomain.com.conf and yourdomain.com-ssl.conf.

Backup above config first!!!

Usually, the usage of www and non-www is: when it found a www, it redirected to non-www, or vice versa. You first can configure in 000-default.conf:

<VirtualHost *:80>
   ServerAdmin webmaster@localhost
   DocumentRoot /var/www/public
   ServerName www.yourdomain.com

   <Directory /var/www/public>
        Options Indexes FollowSymLinks
        AllowOverride None
        Require all granted

        Order Allow,Deny
        Allow from All

        FallbackResource /index.php
   </Directory>

    Redirect / https://yourdomain.com/

    ErrorLog ${APACHE_LOG_DIR}/error.log
    CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>

<VirtualHost *:80>
    ServerAdmin webmaster@localhost
    DocumentRoot /var/www/public
    ServerName yourdomain.com

    <Directory /var/www/public>
        Options Indexes FollowSymLinks
        AllowOverride None
        Require all granted

        Order Allow,Deny
        Allow from All

        FallbackResource /index.php
    </Directory>

    Redirect / https://yourdomain.com/

    ErrorLog ${APACHE_LOG_DIR}/error.log
    CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>

Above config will redirect non-https www and non-www to https://yourdomain.com .

Now, let’s configure default-ssl.conf as follow:

<IfModule mod_ssl.c>

    <VirtualHost _default_:443>
        ServerAdmin webmaster@localhost
        ServerName www.yourdomain.com
        DocumentRoot /var/www/public

        <Directory /var/www/public>
            Options Indexes FollowSymLinks
            AllowOverride None
            Require all granted

            Order Allow,Deny
            Allow from All

            FallbackResource /index.php
        </Directory>

        Redirect / https://yourdomain.com/

        ErrorLog ${APACHE_LOG_DIR}/error.log
        CustomLog ${APACHE_LOG_DIR}/access.log combined

        SSLEngine on

        SSLCertificateFile    /etc/letsencrypt/live/yourdomain.com/fullchain.pem
        SSLCertificateKeyFile /etc/letsencrypt/live/yourdomain.com/privkey.pem

        <FilesMatch "\.(cgi|shtml|phtml|php)$">
            SSLOptions +StdEnvVars
        </FilesMatch>
        <Directory /usr/lib/cgi-bin>
            SSLOptions +StdEnvVars
        </Directory>
    </VirtualHost>

    <VirtualHost _default_:443>
        ServerAdmin webmaster@localhost
        ServerName yourdomain.com
        DocumentRoot /var/www/public

        <Directory /var/www/public>
            Options Indexes FollowSymLinks
            AllowOverride None
            Require all granted

            Order Allow,Deny
            Allow from All

            FallbackResource /index.php
        </Directory>

        ErrorLog ${APACHE_LOG_DIR}/error.log
        CustomLog ${APACHE_LOG_DIR}/access.log combined

        SSLEngine on

        SSLCertificateFile    /etc/letsencrypt/live/yourdomain.com/fullchain.pem
        SSLCertificateKeyFile /etc/letsencrypt/live/yourdomain.com/privkey.pem

        <FilesMatch "\.(cgi|shtml|phtml|php)$">
            SSLOptions +StdEnvVars
        </FilesMatch>
        <Directory /usr/lib/cgi-bin>
            SSLOptions +StdEnvVars
        </Directory>
    </VirtualHost>

</IfModule>

Above config set certificate to location the Certbot print the SSL key. As the SSL is always catched too early, both www and non-www need to have SSL key config!

Ok, configuration seems complete, let’s run the following commands to enable the configs:

sudo ufw allow in "Apache Full"
sudo a2enmod ssl
sudo a2enmod headers
sudo a2enconf ssl-params

# enable default-ssl.conf
# if you have different name, then provide different name
sudo a2ensite default-ssl

# you can add as many config as you created, eg:
# sudo a2ensite yourdomain.com.conf
# sudo a2ensite yourdomain.com-ssl.conf

sudo apache2ctl configtest

Now, its done, time to test:

/usr/bin/systemctl reload apache2 && /usr/bin/systemctl restart apache2

Let’s open the site:

1. http://www.yourdomain.com > redirected to > https://yourdomain.com
2. http://yourdomain.com > redirected to > https://yourdomain.com
3. https://www.yourdomain.com > redirected to > https://yourdomain.com
4. https://yourdomain.com don't redirect as already in target.

and if you’re lucky, you will get redirected to SSL with configured SSL success!

Note:

Let’s Encrypt has expiration on 3 months, and you need to re-generate it before it expired, you can re-generate it monthly via cronjob if needed:

Run crontab -e command and add the following entry:

0 0 1 * *       echo "2" | /usr/bin/certbot certonly --apache --domains yourdomain.com,www.yourdomain.com && /usr/bin/systemctl reload apache2 && /usr/bin/systemctl restart apache2

that will regenerate next month at date 1th at 00:00, and next month, repetitively, and save (CTRL + X if you use nano, or !wq if you’re using vim).

That’s it 😉