Windows/Apache Web Server

Enabling SSL


In 2010, the idea of "HTTPS everywhere" was born by the Electronic Frontier Foundation (EFF), in collaboration with the Tor Project. In 2014, Google announced that websites using encryption woud get a slightly higher rank in its search algorithms. In 2016, Google updated its Chrome browser to explicitly identify sites that do not use HTTPS as "Unsecure".

None of which bothered me much as I do not ask for information from my visitors that requires a secure connection and my websites do not send anything worth securing. I knew it was something I should do, but never got around to it. In October 2022, I finally decided I should install the SSL certificates and use HTTPS instead of plain HTTP.

It took a couple of hours to do because most of the installers and documentation are written for Linux not Apache on Windows that I use. Specifically, I use the Apache installers from Apache Haus. Some of the documentation I found referred to different file formats which confused me no end. It did not help that I run multiple websites on the server and not just the one that most installation help articles use.

I persevered and finally got the certificates and HTTPS to work on my server. This page was written to document how I did it. My thanks to MikeMcQ in the Certbot Community Forum for his patience and who set me straight on a couple of concepts that I did not understand properly when I started this and which caused an earlier version of this page to be misleading. They weren't exactly wrong in that they worked, but they were not the best way to go about these tasks with Certbot.

This page attempts to explain how I obtained and installed the certificates.

Obtaining the SSL Certificates

Looking around the internet I saw that Let's Encrypt is a widely trusted certificate authority (CA). To download and verify them I needed an Automatic Certificate Management Environment (ACME) client. Let's Encrypt lists loads of them but recommends Certbot. That might have been a mistake because unlike some Windows clients, although Certbot downloads the files flawlessly to its own directories, it cannot write to the Apache configuration files to install the certificates properly. That has to be done manually.

Cerbot's documentation specifically says that:

Certbot for Windows currently cannot install the certificate in Apache or Nginx for you. As of the most recent release, you will have to edit your web server application’s configuration to install the certificate yourself after Certbot has obtained it.

At the time, I didn't realize the full implications of that. The word "install" in the above is a little confusing. The certificates do not have to be installed like a program has to be, but Apache needs its configuration files edited so it can find them, and Certbot cannot do that on Windows. That is what the text is referring to.

Certbot is a command line program and Cmd must be run as an administrator for it to work. Just find Cmd in the Start menu and right click on it and choose "Run as administrator"

Running the command prompt as administrator

Running the command prompt as administrator

What I did

For the method I chose to use Certbot, the Apache service must be stopped. This is because using the --standalone option, Certbot needs access to port 80.

Once the command prompt was open, I typed:

certbot certonly --standalone

Certbot is interactive and will ask you to enter a space or comma delimited list of domains for it to get certificates for. These are downloaded together in fullchain.pem. Following the onscreen instructions Certbot will download the files and put them in its directories. The files will be in the "live" directory which are symbolic links to the "archive" directory.

The Certbot directoriesr

The Certbot directory structure
The actual certificates are in the archive folder, but there are symlinks to the latest ones in the live folder,
this is where the Apache configuration files should point to.


The first time I asked for the certificates, I made several mistakes:

I used the certbot --standalone method which involved stopping the Apache service.

When getting the certificates I did not include the www versions of the domain names. I deleted the original certificate and got a new one with the www domains as well.

Once I had the certificates, I copied the files fullchain.pem and privkey.pem to Apache's conf/ssl folder and rewrote the Apache configuration files to point to those. I should have left them where they were and pointed Apache to Certbot's "live" folder which contains symbolic links to the "archive" directory.

What I did worked and both Qualys SSL Labs and Ionos SSL Certificate Checker verified the certificates were installed and working properly, but this was not the best way to obtain the certificates.

What I should have done

Configuring SSL with Certbot for Apache on Windows

My main problem when I started this in October 2022, was that I didn't know what I was doing and didn't read the documentation properly before I installed Certbot and got the certificates. Luckily Let's Encrypt emails users when the certificates need renewing in less than a month, so I was able to renew the certificates manually by stopping the Apache server and in a command prompt with Administrator privileges using certbot renew and then restarting the server.

In May 2023, I revisited the issues to see if I could get all of this done automatically and without stopping and restarting the server.

It helped that I used the Apache Haus Apache installers because that is mostly set up for SSL already. The Certbot documentation "Where are my certificates?", says to point the server to Certbot's folders rather than copy fullchain.pem and privkey.pem to the Apache conf/ssl directory which I had been doing.

I had originally copied fullchain.pem and privkey.pem from Certbot's folders and put them in Apache's conf/ssl directory, then used lines such as SSLCertificateFile "${SRVROOT}/conf/ssl/fullchain.pem" and SSLCertificateFile "conf/ssl/fullchain.pem" in Apache's configuration file. This was wrong and instead I should have simply pointed Apache to the certificates in Certbot's folders:

Open httpd-ssl.conf for editing. In the Server Certificate: section add the line:

SSLCertificateFile "full-absolute-path-to-Certbot/live/fullchain.pem"

Then comment out or delete the other directives in the section

In the Server Private Key: section add the line:

SSLCertificateKeyFile "full-absolute-path-to-Certbot/live/privkey.pem"

Then comment out or delete the other directives in the section and save the file

Open httpd-vhosts.conf for editing and in every virtual host entry, including the default, change the listening port to 443, the default port for HTTPS transfers, then add the lines:

SSLEngine on
SSLCertificateFile "full-absolute-path-to-Certbot/live/fullchain.pem"
SSLCertificateKeyFile "full-absolute-path-to-Certbot/live/privkey.pem"

Each virtual host needs a new block of directives to listen on port 80 and redirect it to the https version. So for each virtual host create this:

VirtualHost *:80
ServerAlias */
Redirect permanent /

Then save the file and restart the Apache service so the changes can take affect.

The trailing slash on the redirects is needed to handle subdirectories. Without it, people get redirected to the homepage.

Updating the SSL Certificates

When Certbot is installed it creates a task in the scheduler to look for new certificates and download any new ones twice a day. The command in Task Manager is:

Powershell.exe -NoProfile -WindowStyle Hidden -Command "certbot renew"

The command doesn't work. Whenever it runs, Windows pops up a notification asking what to do with the file:

Windows asking what to do with the Certbot task

Windows asking what to do with the Certbot task

One way to get this to work properly is to stop Apache, run certbot renew, then restart the server.

Here's a PowerShell script that will do that:

New-Variable -Name serviceName -Value 'Apache2.4' -Option Constant
Write-Output "Stopping Apache"
Stop-Service -name $serviceName
$service = Get-Service -Name $serviceName
Write-Output "Apache is stopped"

# The Lines below work and waits for certbot to finsh before restarting Apache
# Uncomment any 1 of the 3 following lines - they all work
# certbot.exe renew
# Invoke-expression -command "certbot.exe renew"
# Start-Process -FilePath "certbot.exe" -ArgumentList "renew" -wait

Write-Output "Apache is restarting"
Start-Service -Name $serviceName
Write-Output "Apache has restarted"

If you prefer, here's a plain batch file that works. It works because net waits before the service is stopped before carrying on to the next line.

net stop Apache2.4
certbot.exe renew
net start Apache2.4

The PowerShell script or batch file must be run as an administrator to work. You may need to change the name of the service to be stopped, for example from Apache2.4 to httpd

As a reminder the certbot commands should all be on one line, and the scripts need to be run with administrator privileges. Another method that works is to use certbot certonly --webroot

Unlike the previous script where there were three command choices to run Certbot, there is only one method I found that worked for this:

Start-Process -FilePath "certbot.exe" -ArgumentList "certonly --webroot -w C:\Apache24\htdocs\brisray -d -d -w C:\Apache24\htdocs\hmsgambia -d -d -w C:\Apache24\htdocs\icehouseoffroad -d -d" -wait
Restart-Service -Name Apache2.4

In the above version, the root of each domain has to be given along with the domains for that root. It also does not appear to check if the certificates are near their expiry date.

If you prefer, here's a plain batch file that works. There is no net restart command, so net stop is followed by a net start.

certbot certonly --webroot -w C:\Apache24\htdocs\brisray -d -d -w C:\Apache24\htdocs\hmsgambia -d -d -w C:\Apache24\htdocs\icehouseoffroad -d -d
net stop Apache2.4
net start Apache2.4


Updating Certbot

I was originally using Certbot version 1.31.0 which was released on October 4, 2022, version 2.6.0 was released on May 9, 2023. Updating was simply downloading the latest release and running the installer. The certificate files are not affected by the upgrade.

Open Port 443

Port 443 is the default port for secure HTTPS transfers and it should be open on your router port forwarding section of its control panel and it should point to the IP address of your web server.

Port 443 open on the router

Port 443 open on the router

Testing the Configuration

It is difficult to tell whether HTTPS is working properly when sitting at the server, but there are a lot of websites that can check your site and its certificates for you.Just search for "ssl checker". If everything is configured properly then you should see something like this:

Qualys SSL Labs

My result from Qualys SSL Labs

Ionos SSL Certificate Checker

My result from Ionos SSL Certificate Checker

HTTP to HTTPS Without a Certificate

3 out of 4 isn't bad

One of my sites is a little weird in the way it was set up years ago. I've got the domain name and a friend of mine registered which points to the server. Because I don't own that domain name I cannot get a SSL certificate for it. Because the https handshaking occurs before any files are exchanged, I cannot redirect to Redirect permanent / works for the http version of but there's no way it will work for the https version which browsers report as "This site can’t be reached".

Unsecured Content

The website checker Detectify once offered free checks for personal, non-commercial sites. I took advantage of that and was grandfathered in when they no longer offered it. Shortly after I added the SSL certificate they did a scan of the site and something new turned up in the report they make. Three of my pages came up with the warning that there were files being served as Mixed Content (HTTPS vs HTTP). Looking at the report it said that the three pages were displaying HTTP content through the HTTPS connection. What was happening?

Mixed Content (HTTPS vs HTTP) warning from Detectify

Mixed Content (HTTPS vs HTTP) warning from Detectify

Chrome, my usual browser had the usual secured padlock, so I tried the site Edge, Firefox, and Opera. The browser that showed any sign that something was amiss was Firefox and that showed a small warning triangle by the padlock.

Firefox padlocks: secure and with a warning

Firefox padlocks: secure and with a warning

Why was this HTTP content still displaying and why didn't the browsers seem to treat it very seriously?

I opened the developer section of the browsers by pressing F12, and they all seemed to say the same thing:

Mixed Content: The page was loaded over HTTPS, but requested an insecure element. This request was automatically upgraded to HTTPS, For more information...

I took a look the pages and sure enough there were HTTP links in them. The browsers had automatically changed those to HTTPS requests and downloaded the content. They were only images, but would they have issued a stronger warning or not download the content at all if a HTTPS connection was not available or if the link was to a different file type?

As there were no warnings given by most browsers, I looked around to see if there was some sort of online checker. There are sites that will check individual pages for HTPP vs HTPPS content, but there are others that will attempt to crawl an entire site. Two of the best I have found are JitBit SSL Check and Missing Padlock - SSL Checker. HTTPS Checker has a free downloadable checker. It works very well but the free edition is limited to 500 pages, this is OK for my sites.

These tools are very useful for finding mixed content, and especially useful for finding pages that now have content completely blocked by my sites' Content Security Policy (CSP) rules. Some pages that were flagged by these tools was found to be missing content. The browser developer tools gave the following error:

Mixed Content: The page was loaded over HTTPS, but requested an insecure plugin resource. This request has been blocked; the content must be served over HTTPS.

Needless to say, the pages hghlighted by Detectify and the other tools are being checked and the links upgraded.

Google Search Console

My sites use Google Search Console and I got a shock when I visited the statistics pages a little while after installing the SSL Certificates - both the Google search impressions and traffic being sent from them had dropped to next to zero!

The statistics in Google Search Console dropped to next to zero after installing the SSL Certificates

The statistics in Google Search Console dropped to next to zero after installing the SSL Certificates

What was happening?

It happened because of the way I originally added the sites, I used the domain name starting with HTTP; after adding the SSL Certifcates the sites now use HTTPS and are not being recorded by the Console.

What do do about it?

A new property has to be created. Do this by clicking on the dropdown in the top left of the Google Search Console page and choose "Add property" from the dropdown.

The Google Search Console 'Add property' dropdown

The Google Search Console "Add property" dropdown

Recently Google Search Console added a choice of what happens next. You can add a URL prefix, for example HTTPS, or you can register the entire domain. As I do not use subdomains, I opted to register the entire domain for each of my sites. My choice just requires the main domain name without HTTP or HTTPS. In order to do this you must have access to the DNS records for the sites because those need to be edited.

The Google Search Console property types

The Google Search Console property types

The Google Search Console will then give you a verification code that needs to be pasted into the DNS TXT record for the domain. There may be problems doing this as explained in Multiple TXT fields for same subdomain. Some registrars will allow separate entries with each entry enclosed in quotes, others will allow each entry on new lines, others need a new text record.

I found that for my DNS records that I needed to created a TXT record rather than trying to edit an existing one...

Adding a TXT entry to a DNS record

Adding a TXT entry to a DNS record

Once the entry has been made, Google Search Console will then verify that it has been done properly. One nice thing is that the sitemap used by them will be copied to the new property. The bad news is that the statistics will start afresh for the new property and will take a while to populate properly.

Google Search Console domain ownership verification

Google Search Console domain ownership verification

If there is an error verifying the domain, one of the other methods must be used as discussed above.

Sources and Resources

Apache Haus
Certbot Community Forum
Certbot Documentation
Certbot Latest Releases
Electronic Frontier Foundation
HTTPS Checker - Checks a HTTPS site for insecure content. The online checker is not free but the downloadable one is
Ionos SSL Certificate Checker - checks that the certificates are installed properly
JitBit SSL Check - Checks a HTTPS site for insecure content
Let's Encrypt
Missing Padlock SSL Checker - Checks a HTTPS site for insecure content
Multiple TXT fields for same subdomain (Stack Exchange / Server Fault) - Adding TXT records to the DNS records
Qualys SSL Server Test - checks that the certificates are installed properly
SSL Certificate Formats (Tutorials Teacher)
Tor Project

This page created October 22, 2022; last modified May 29, 2023