1. Home
  2. Tutorials
  3. Linux Website Configuration
  4. Apache HTTPS encryption
Yolinux.com Tutorial

Apache HTTPS Virtual Host Configuration:

Web server configuration for encryption support for multiple virtual host website domains on one IP address.

Apache HTTPS Intro:

Reasons to add HTTPS encryption to a website:

  • Protect private user data entered into forms, as it travels from browser to server
  • Keep user navigation and content subject matter private
  • Improve user perception that the website is safe. Most users haven't a clue but rather than have them be alarmed by an icon labeling a website as unencrypted, it is easier to add HTTPS
  • Perceived SEO benefits (search rank)

Prerequisites:

This tutorial assumes that one has a Bind based DNS configured for the domain as well as the Apache web server configured for virtual hosting of web content. See the following tutorial on Bind DNS and Apache Web Site Configuration on Linux.

Certificates:

This tutorial will cover the installation and use of encryption keys and certificates from LetsEncrypt.org which are available for free for a duration of 90 days after which they must be renewed. The TLS or SSL (TLS is a newer and improved version of encryption than SSL) keys, certificates and CA chain files are obtained from the LetsEncrypt.org server using a client script which employs the Automatic Certificate Management Environment (ACME) protocol. The ACME protocol supports deployment between the Certificate authority (CA), the entity that stores, signs and issues digital certificates to certify the ownership of a public key, and a user's web server. ACME version 2 supports wildcards: *.megacorp.com. This allows the domain certificates obtained to support megacorp.com, www.megacorp.com, mail.megacorp.com and all nodes supporting the domain.

ACME Client:

An ACME client is run to obtain browser trusted certificates from Let's Encrypt. The issued certificate can be used for SSL/TLS for use by web servers, mail servers (but not for email encryption), ftp servers, etc.

When verifying domain ownership, the user must show a working DNS or web server configuration to show that they control the domain. This tutorial assumes the use of Bind as a DNS name server if using DNS for domain verification or use of the Apache web server for HTTP domain verification.

Apache Configuration:

This tutorial will show the additional required configuration settings to an existing "Virtual Hosts" HTTP web server. See the YoLinux Linux Internet Web Server and Domain Configuration Tutorial for instructions on configuring bind DNS and Apache.

Three files are referenced by the Apache server for an HTTPS website:

  • SSLCertificateFile: crt fle
  • SSLCertificateKeyFile: key file
  • SSLCertificateChainFile: crt file. This file defines the certificate authorities (CAs) hierarchy chain of trust from the root CA through the intermediate CAs to the end user certificate. The browser will travers this chain until a trusted CA is found after which a secure connection will be established. The browser installation includes the root certificates. The SSLCertificateChainFile defines the intermediate certificates.

ACME Client for Encryption Certificate Generation:

ACME Client:

The Let's Encrypt website lists a lot of eligible ACME clients

Using the bash script getssl:

  1. Install getssl
  2. Generate getssl configuration files and edit
  3. Request certificate generation
  4. Create DNS record for domain ownership verification
  5. Configure Apache web server for HTTPS using the certificates generated

Install getssl from github: https://github.com/srvrco/getssl

curl --silent https://raw.githubusercontent.com/srvrco/getssl/latest/getssl > getssl ; chmod 700 getssl

Also download from github:

File: /opt/bin/dns_add_manual
#!/usr/bin/env bash

echo "In the DNS, a new TXT record needs to be created for;"
echo "_acme-challenge.${1}"
echo "containing the following value"
echo "$2"

read -r -p "Press any key to obtain the certificate once the records have been updated..."
File: /opt/bin/dns_del_manual
#!/usr/bin/env bash

echo "In the DNS, the following DNS record should be deleted ;"
echo "_acme-challenge.${1}"

read -r -p "Press any key to obtain the certificate once the records have been updated..."
usage:
  • -c: create default config files to edit
  • -q: Quiet mode
  • -h: Help

Certificate Generation and Deployment Options:

There are two primary options:

  1. Generate certificates for base and www domains:
    • megacorp.com
    • www.megacorp.com
    This option allows for domain verification by issuing a file generated and placed in the root document directory which can be accessed via HTTP by the issuing certificate authority. Certificate renewal can be automated using cron.
  2. Generate certificates for base and all wildcard "*" domains:
    • megacorp.com
    • *.megacorp.com
    This option requires domain verification by issuing a DNS text string which can be accessed via a DNS query by the issuing certificate authority. For systems using bind as a DNS name server, this is a manual process and harder to automate certificate renewals.
1) HTTPS for Root Domain and www Subdomain:

This approach supports website access using the base domain (eg. https://megacorp.com) or www subdomain (eg. https://www.megacorp.com) as the URL to access a website.

Create initial configuration files for the domain: getssl -c megacorp.com
Making domain directory - /home/user1/.getssl/megacorp.com
creating domain config file in /home/user1/.getssl/megacorp.com/getssl.cfg
created domain config file in /home/user1/.getssl/megacorp.com/getssl.cfg

Will generate the following configuration files:

  • ~/.getssl/getssl.cfg - global server config file
  • ~/.getssl/megacorp.com/getssl.cfg - edit this domain specific file
Edit global config for server: ~/.getssl/getssl.cfg
CA="https://acme-v02.api.letsencrypt.org"
ACCOUNT_EMAIL="user1@megacorp.com"
ACCOUNT_KEY_LENGTH=4096
ACCOUNT_KEY="/home/user1/.getssl/account.key"
ACCOUNT_KEY_TYPE="rsa"
PRIVATE_KEY_ALG="rsa"
FULL_CHAIN_INCLUDE_ROOT="true"
RELOAD_CMD="sudo apachectl restart"
RENEW_ALLOW="90"
SERVER_TYPE="https"
CHECK_REMOTE="true"
VALIDATE_VIA_DNS="false"
DNS_ADD_COMMAND="/opt/bin/dns_add_manual"
DNS_DEL_COMMAND="/opt/bin/dns_del_manual"
PUBLIC_DNS_SERVER="XXX.XXX.XXX.XXX"
AUTH_DNS_SERVER="XXX.XXX.XXX.XXX"

Note that the renew period was increased to the maximum of 90 days (max for letsencrypt.org).

Must set VALIDATE_VIA_DNS to false in order to validate via http your authority by installing the verification files (files with encrypted hash as a file name) in the directory /.well-known/acme-challenge/ in your web content root directory. The presence of the files are what is being validated by the certificate authority.

Edit (or use default) this domain specific file: ~/.getssl/megacorp.com/getssl.cfg

CA="https://acme-v02.api.letsencrypt.org"
PRIVATE_KEY_ALG="rsa"
SANS="www.megacorp.com"
ACL=('/home/user1/www/megacorp.com/html/.well-known/acme-challenge')
USE_SINGLE_ACL="true"
DOMAIN_CERT_LOCATION="/etc/ssl/websites/megacorp.com/megacorp.com.crt"
DOMAIN_KEY_LOCATION="/etc/ssl/websites/megacorp.com/megacorp.com.key"
DOMAIN_CHAIN_LOCATION="/etc/ssl/websites/megacorp.com/fullchain.crt"
RELOAD_CMD="sudo apachectl restart"
SERVER_TYPE="https"

The SANS is set to support a single subdomain (www). This can be a comma separated list of additional domains / subdomains. This should not include the primary domain (eg. megacorp.com).

Now is a good time to edit the Apache website conf file (see below).

Run the script; ./getssl megacorp.com
megacorp.com: Certificate on remote domain does not match, ignoring remote certificate (host.name-of-computer.com != megacorp.com)
creating key - /home/user1/.getssl/megacorp.com/megacorp.com.key
Generating RSA private key, 4096 bit long modulus
..............................................................................................................................++
............................................................................................................................++
e is 65537 (0x10001)
creating domain csr - /home/user1/.getssl/megacorp.com/megacorp.com.csr
Registering account
Verify each domain
Verifying megacorp.com
copying challenge token to /home/user1/www/megacorp.com/html/.well-known/acme-challenge/asdfJNzw934567rKbeJqVVa2Wepl_z4fUohjkljjzD0
sending request to ACME server saying we're ready for challenge
checking if challenge is complete
Pending
checking if challenge is complete
Verified megacorp.com
Verifying www.megacorp.com
copying challenge token to /home/user1/www/megacorp.com/html/.well-known/acme-challenge/eP40xphT2134rkCwPjp7fghjk4SbmuzMUZasdfV5JAc
sending request to ACME server saying we're ready for challenge
checking if challenge is complete
Pending
checking if challenge is complete
Verified www.megacorp.com
Verification completed, obtaining certificate.
Requesting Finalize Link
Requesting Order Link
Requesting certificate
Certificate saved in /home/user1/.getssl/megacorp.com/megacorp.com.crt
copying domain certificate to /etc/ssl/websites/megacorp.com/megacorp.com.crt
copying private key to /etc/ssl/websites/megacorp.com/megacorp.com.key
copying full chain to /etc/ssl/websites/megacorp.com/fullchain.crt
reloading SSL services
megacorp.com - rsa certificate installed OK on server
certificate obtained for megacorp.com

Automating Certificate Renewal:

File: /etc/crontab

Add the following line.

0 18 1 */2 * root /opt/bin/getssl -u -a -q &>> /var/log/getssl.log
where:
  • crontab:
    • 0 : zero minutes (0 - 59)
    • 18 : hour of the day is 6pm during the day (0 - 24)
    • 1 : day of month (1 - 31)
    • */2 : every two months. Execute in a specific month (1 -12)
    • * : wildcard "*" refers to any day of the week (0 - 6)
    • run as user specified
  • getssl arguments:
    • -a, --all : Renew all certificates
    • -u, --upgrade : Upgrade getssl if a more recent version is available - can be used with or without domain(s)
    • -q, --quiet : Quiet mode (only outputs on error, success of new cert, or getssl was upgraded)
File: /etc/logrotate.d/getssl
/var/log/getssl.log {
    yearly
    rotate 10
    copytruncate
    delaycompress
    compress
    notifempty
    missingok
}
2) HTTPS for Root Domain and All * Subdomains:

This approach supports wildcards and thus requires adding a DNS record to verify domain ownership.

Create initial configuration files for the domain: getssl -c megacorp.com
creating main config file /home/user1/.getssl/getssl.cfg
Making domain directory - /home/user1/.getssl/megacorp.com
creating domain config file in /home/user1/.getssl/megacorp.com/getssl.cfg
Adding SANS=*.megacorp.com from certificate installed on megacorp.com to new configuration file
created domain config file in /home/user1/.getssl/megacorp.com/getssl.cfg

May take a few seconds to run.

Will generate the following configuration files:
  • ~/.getssl/getssl.cfg - global server config file
  • ~/.getssl/megacorp.com/getssl.cfg - edit this domain specific file
Edit global config for server: ~/.getssl/getssl.cfg
CA="https://acme-v02.api.letsencrypt.org"
ACCOUNT_EMAIL="user1@megacorp.com"
ACCOUNT_KEY_LENGTH=4096
ACCOUNT_KEY="/home/user1/.getssl/account.key"
ACCOUNT_KEY_TYPE="rsa"
PRIVATE_KEY_ALG="rsa"
FULL_CHAIN_INCLUDE_ROOT="true"
RELOAD_CMD="apachectl restart"
RENEW_ALLOW="90"
SERVER_TYPE="https"
CHECK_REMOTE="true"
VALIDATE_VIA_DNS="true"
DNS_ADD_COMMAND="/opt/bin/dns_add_manual"
DNS_DEL_COMMAND="/opt/bin/dns_del_manual"
PUBLIC_DNS_SERVER="XXX.XXX.XXX.XXX"
AUTH_DNS_SERVER="XXX.XXX.XXX.XXX"

Tip for AWS EC2 users. Use public "elastic" IP addresses rather than private VPC (Virtual Private Cloud) IP addresses.

Note that the renew period was increased to the maximum of 90 days.

Must set VALIDATE_VIA_DNS to true or you will get the following error:
megacorp.com: cannot use http-01 validation for wildcard domains

 

Edit (or use default) this domain specific file: ~/.getssl/megacorp.com/getssl.cfg
CA="https://acme-v02.api.letsencrypt.org"
PRIVATE_KEY_ALG="rsa"
SANS="*.megacorp.com"
RELOAD_CMD="apachectl restart"
SERVER_TYPE="https"

The SANS is set to support wildcards so that all network nodes for the domain are included.

Run the script; ./getssl megacorp.com
megacorp.com: Certificate on remote domain does not match, ignoring remote certificate ( != megacorp.com)
creating account key /home/user1/.getssl/account.key
creating key - /home/user1/.getssl/account.key
Generating RSA private key, 4096 bit long modulus
.....................++
........................................................++
e is 65537 (0x10001)
creating key - /home/user1/.getssl/megacorp.com/megacorp.com.key
Generating RSA private key, 4096 bit long modulus
...................................................................................................................................................................++
.............++
e is 65537 (0x10001)
creating domain csr - /home/user1/.getssl/megacorp.com/megacorp.com.csr
Registering account
Registered
Verify each domain
Verifying megacorp.com
In the DNS, a new TXT record needs to be created for;
_acme-challenge.megacorp.com
containing the following value
X93NZI6785A63HB4wazUFdZhj6qtyopwERpp6QLTCRk
Press any key to obtain the certificate once the records have been updated...

It is at this point in running the getssl script that one will have to make an entry in the DNS configuration. The purpose is to show and verify to the certificate authority (CA) that you legitimately own and control the domain. Add a TX entry to the domains DNS record. Here we show a text entry for a bind DNS configuration:

File: /var/named/chroot/var/named/data/named.megacorp.com
...
....
               2014123113 ; serial

...
...

_acme-challenge.megacorp.com. IN TXT "X93NZI6785A63HB4wazUFdZhj6qtyopwERpp6QLTCRk"

Remember to increment the serial value.

Note that there is a "." at the end of the challenge value: _acme-challenge.megacorp.com.

Note that there may be multiple "_acme-challenge" TXT entries.

Restart bind: service named restart

Verify: dig +trace _acme-challenge.megacorp.com

Return to the terminal window running getssl and press enter.

checking DNS at XXX.XXX.XXX.XXX
checking DNS at XXX.XXX.XXX.XXX
sleeping 60 seconds before asking the ACME server to check the dns
sending request to ACME server saying we're ready for challenge
checking if challenge is complete
Pending
checking if challenge is complete
Verified megacorp.com
In the DNS, the following DNS record should be deleted ;
_acme-challenge.megacorp.com
Press any key to obtain the certificate once the records have been updated...
Press "Enter"
Verifying *.megacorp.com
In the DNS, a new TXT record needs to be created for;
_acme-challenge.megacorp.com
containing the following value
GPm2_R6W1bPKh2FQ1234EnJsTdQDtf6Ifk5Uqf85y_s
Press any key to obtain the certificate once the records have been updated...

Add another record to your DNS.

Return to the terminal window running getssl and press "Enter".

checking DNS at XXX.XXX.XXX.XXX
checking DNS at XXX.XXX.XXX.XXX
sleeping 60 seconds before asking the ACME server to check the dns
sending request to ACME server saying we're ready for challenge
checking if challenge is complete
Pending
checking if challenge is complete
Verified *.megacorp.com
In the DNS, the following DNS record should be deleted ;
_acme-challenge.megacorp.com
Press any key to obtain the certificate once the records have been updated...

Remove previously added DNS records.

Return to the terminal window running getssl and press "Enter".

Verification completed, obtaining certificate.
Requesting Finalize Link
Requesting Order Link
Requesting certificate
Certificate saved in /home/user1/.getssl/megacorp.com/megacorp.com.crt
reloading SSL services

Delete the bind DNS TXT "_acme-challenge" line, increment the serial value and restart bind (service named restart).

At this point the encryption certificates will be generated and will be ready for use by the Apache web server.

Copy encryption certificates to an Apache accessible directory.

sudo cp '.getssl/megacorp.com/megacorp.com.key' /etc/ssl/websites/megacorp.com/megacorp.com.key
sudo cp '.getssl/megacorp.com/megacorp.com.crt' /etc/ssl/websites/megacorp.com/megacorp.com.crt
sudo cp '.getssl/megacorp.com/fullchain.crt' /etc/ssl/websites/megacorp.com/fullchain.crt
Apache HTTPS Configuration:

Configure Apache for multiple HTTPS domains on the same IP, using virtual hosts with Server Name Indication (SNI) to allow the server to determine the correct certificate for each domain.

Edit the configuration for the domain by adding the HTTPS configuration to the HTTP configuration. The working HTTP configuration is not altered. One will add an HTTPS configuration.

File: /etc/httpd/sites.d/megacorp.conf
...
...

<VirtualHost *:443>
        ServerName  megacorp.com
        ServerAlias www.megacorp.com
        DocumentRoot /home/websites/www/megacorp.com/html/
        <Directory "/">
                Options FollowSymLinks
                AllowOverride None
        </Directory>
        <Directory /home/websites/www/megacorp.com/html>
                XBitHack Full
                Options FollowSymLinks MultiViews Includes
                IndexOptions SuppressLastModified SuppressDescription
                AllowOverride All
                Order allow,deny
                allow from all
                Require all granted
        </Directory>
        SSLEngine on
        SSLProtocol all -SSLv3 -TLSv1
        SSLCertificateFile /etc/ssl/websites/megacorp.com/megacorp.com.crt
        SSLCertificateKeyFile /etc/ssl/websites/megacorp.com/megacorp.com.key
        SSLCertificateChainFile /etc/ssl/websites/megacorp.com/fullchain.crt

        ErrorLog /var/log/httpd/megacorp.com-error.log
        LogLevel warn
        CustomLog /var/log/httpd/megacorp.com-access.log combined
        ServerSignature Off
</VirtualHost>

Verify the Apache webserver configuration: apachectl configtest
Look for the response Syntax OK

Restart the Apache webserver: apachectl restart

Debugging HTTPS:

Firewall Rules:

[Pitfall] The webpage does not load but stalls and reaches timeout. Try flushing the firewall rules on the web server: iptables -F

Apache Restart Fails:

[Pitfall] Restart (apachectl restart) fails and give the following error:

(13)Permission denied: AH00058: Error retrieving pid file /var/run/httpd/httpd.pid
AH00059: Remove it before continuing if it is corrupted.
getssl: error running: apachectl restart
One must be root user in order to restart the server.

Check Virtual Hosts:

apachectl -t -D DUMP_VHOSTS | grep megacorp

         port 443 namevhost megacorp.com (/etc/httpd/sites.d/megacorp.conf:140)
                 alias www.megacorp.com
         port 80 namevhost megacorp.com (/etc/httpd/sites.d/megacorp.conf:1)
                 alias www.megacorp.com
                 alias aws1.megacorp.com
         port 80 namevhost megacorp.com (/etc/httpd/sites.d/megacorp.conf:44)
                 alias mail.megacorp.com

Check Error Logs:

File: /var/log/httpd/megacorp.com-error.log

[Mon Mar 31 06:30:08.280026 2025] [ssl:warn] [pid 26852:tid 26852] AH01909: megacorp.com:443:0 server certificate does NOT include an ID which matches the server name

Examine the certificate: openssl x509 -text -in /etc/ssl/websites/megacorp.com/megacorp.com.crt

Look for: "Subject: CN=megacorp.com"
and SAN (can contain multiple lines): "X509v3 Subject Alternative Name:" DNS:megacorp.com

This is what a working configuration looks like,
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            05:de:82:...................................:a4:c9:c9
    Signature Algorithm: sha256WithRSAEncryption
        Issuer: C=US, O=Let's Encrypt, CN=R10
        Validity
            Not Before: Mar 31 08:28:08 2025 GMT
            Not After : Jun 29 08:28:07 2025 GMT
        Subject: CN=megacorp.com
...
...
...

            X509v3 Subject Alternative Name: 
                DNS:*.megacorp.com, DNS:megacorp.com
...

Using Curl:

  • Fetch page header:
    • curl -I https://megacorp.com
    • curl -I https://www.megacorp.com
    HTTP/1.1 200 OK
    Date: Sun, 30 Mar 2025 23:06:24 GMT
    Server: Apache/2.4.61 (Amazon) OpenSSL/1.0.2k-fips PHP/5.6.40
    Last-Modified: Tue, 15 Oct 2024 09:52:39 GMT
    Accept-Ranges: bytes
    Content-Type: text/html; charset=UTF-8
        
  • Examine certificate:

    Extract CA cert from a server

    • curl -w %{certs} https://megacorp.com > cacert.pem
    • curl -w %{certs} https://www.megacorp.com > cacert.pem
      % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                     Dload  Upload   Total   Spent    Left  Speed
    100  9110    0  9110    0     0  23562      0 --:--:-- --:--:-- --:--:-- 23601
        
  • Man page: curl

Certificate Expiration:

Check certificate expiration date:

  • openssl x509 -text -in chain.crt
  • openssl x509 -text -in domain.crt
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            05:de:82:...................................:a4:c9:c9
    Signature Algorithm: sha256WithRSAEncryption
        Issuer: C=US, O=Let's Encrypt, CN=R11
        Validity
            Not Before: Mar 31 08:28:08 2025 GMT
            Not After : Jun 29 08:28:07 2025 GMT
        Subject: CN=megacorp.com

        ...
        ...

Man page: openssl

Stale Certificate:

Close the browser and then restart the browser application.

Configuration Evaluation:

Here are some websites which can be used to test and examine your HTTPS installation and give an assessment:

System Default Web Server:

The system default website applies HTTPS to the server "localhost" is defined in /etc/httpd/conf/httpd.conf where DocumentRoot "/var/www/html" with HTTPS certificates and keys set in /etc/httpd/conf.d/ssl.conf where the following is defined:

  • SSLCertificateFile /etc/pki/tls/certs/localhost.crt
  • SSLCertificateKeyFile /etc/pki/tls/private/localhost.key
If verification tests show the certificate/keys to be expired, perform the self signing renewal:
sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/pki/tls/private/localhost.key -out /etc/pki/tls/certs/localhost.crt

Where:
  • -x509: Indicates that you are creating a self-signed certificate.
  • -nodes: Prevents the key from being encrypted with a password.
  • -days 365: Sets the certificate's validity period to 365 days.
  • -newkey rsa:2048: Generates a new RSA key with a 2048-bit length.

Links:

Linux documentation:

Free Certificate Authorities::

Books:

"Apache Server Bible 2"
by Mohammed J. Kabir
ISBN # 0764548212, Hungry Minds

This book is very complete covering all aspects in detail. It is not your basic reprint of the apache.org documents like so many others.

Amazon.com

   
Bookmark and Share

Advertisements