Running an HTTPS endpoint protects credentials, cookies, and API payloads from being read or modified in transit. In internal networks and short-lived development environments, a self-signed certificate provides encryption without waiting on external Certificate Authority issuance. The trade-off is trust: browsers and clients warn unless the certificate (or a private CA) is explicitly trusted.
Apache terminates TLS through mod_ssl on port 443, presenting an X.509 certificate during the handshake and using the corresponding private key to prove ownership. Each HTTPS VirtualHost points to a certificate file (commonly .crt) via SSLCertificateFile and a key file (commonly .key) via SSLCertificateKeyFile. A self-signed certificate signs its own public key, so the chain of trust ends at the server unless the certificate is installed as a trust anchor on clients.
Commands and paths target the Ubuntu and Debian Apache packaging, which uses /etc/apache2/ plus the a2enmod and a2ensite helpers. Modern clients validate hostnames from the subjectAltName extension, so relying on a Common Name alone can trigger certificate errors. Keep the private key readable only by root, and use a publicly trusted certificate for Internet-facing sites.
Steps to set up a self-signed TLS certificate for Apache:
- Open a terminal.
- Create a directory for the certificate and private key.
$ sudo mkdir -p /etc/apache2/ssl
- Generate a self-signed certificate and private key with OpenSSL using subjectAltName for the hostname.
$ sudo openssl req -x509 -newkey rsa:2048 -sha256 -days 365 -nodes \ -keyout /etc/apache2/ssl/apache.key -out /etc/apache2/ssl/apache.crt \ -subj "/CN=example.com" -addext "subjectAltName=DNS:example.com" Generating a RSA private key ................................................+++++ ................................................+++++ writing new private key to '/etc/apache2/ssl/apache.key' -----
The --nodes option writes an unencrypted private key, so strict file permissions are required.
Add more names by extending the value, for example subjectAltName=DNS:example.com,DNS:www.example.com.
- Restrict the private key to root-only read access.
$ sudo chmod 600 /etc/apache2/ssl/apache.key $ sudo ls -l /etc/apache2/ssl/apache.key -rw------- 1 root root 1704 Dec 13 12:34 /etc/apache2/ssl/apache.key
- Enable mod_ssl.
$ sudo a2enmod ssl Considering dependency setenvif for ssl: Module setenvif already enabled Considering dependency mime for ssl: Module mime already enabled Considering dependency socache_shmcb for ssl: Enabling module socache_shmcb. Enabling module ssl. See /usr/share/doc/apache2/README.Debian.gz on how to configure SSL and create self-signed certificates. To activate the new configuration, you need to run: systemctl restart apache2
If HTTPS refuses connections, confirm Listen 443 exists in /etc/apache2/ports.conf/.
- Edit /etc/apache2/sites-available/example.com.conf/.
$ sudo vi /etc/apache2/sites-available/example.com.conf
Back up existing site configuration files before edits to avoid overwriting a working configuration.
- Add an HTTPS VirtualHost on port 443 that references the generated certificate and key.
<VirtualHost *:443> ServerName example.com SSLEngine on SSLCertificateFile /etc/apache2/ssl/apache.crt SSLCertificateKeyFile /etc/apache2/ssl/apache.key </VirtualHost>
- Enable the site configuration.
$ sudo a2ensite example.com.conf Enabling site example.com. To activate the new configuration, you need to run: systemctl reload apache2
- Optionally redirect HTTP traffic to HTTPS in the port 80 VirtualHost.
<VirtualHost *:80> ServerName example.com Redirect permanent / https://example.com/ </VirtualHost>
A redirect is best placed in the existing port 80 site for the same ServerName.
- Test the Apache configuration for syntax errors.
$ sudo apache2ctl configtest AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 127.0.1.1. Set the 'ServerName' directive globally to suppress this message Syntax OK
Suppress the AH00558 warning by setting a global ServerName in /etc/apache2/apache2.conf/.
- Restart Apache to load mod_ssl with the updated VirtualHost configuration.
$ sudo systemctl restart apache2
On RHEL-based distributions the service is commonly named httpd.
- Verify TLS negotiation and certificate presentation over HTTPS.
$ curl -kv https://example.com/ * Trying 203.0.113.10:443... * Connected to example.com (203.0.113.10) port 443 (#0) * ALPN: offers h2,http/1.1 ##### snipped ##### * SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 * Server certificate: * subject: CN=example.com * issuer: CN=example.com * SSL certificate verify result: self-signed certificate (18), continuing anyway. > GET / HTTP/1.1 > Host: example.com ##### snipped #####
Remove --insecure from curl after the certificate is trusted by the client.
- Optionally trust the self-signed certificate on a Debian or Ubuntu client to avoid browser warnings.
$ sudo cp /etc/apache2/ssl/apache.crt /usr/local/share/ca-certificates/example.com-self-signed.crt $ sudo update-ca-certificates Updating certificates in /etc/ssl/certs... 1 added, 0 removed; done. Running hooks in /etc/ca-certificates/update.d... ##### snipped ##### done.
Some applications, including Firefox, use a separate trust store that requires an explicit import.
- Verify certificate trust without --insecure after installing the certificate into the trust store.
$ curl -v https://example.com/ * Trying 203.0.113.10:443... * Connected to example.com (203.0.113.10) port 443 (#0) ##### snipped ##### * SSL certificate verify ok. > GET / HTTP/1.1 > Host: example.com ##### snipped #####
Mohd Shakir Zakaria is a cloud architect with deep roots in software development and open-source advocacy. Certified in AWS, Red Hat, VMware, ITIL, and Linux, he specializes in designing and managing robust cloud and on-premises infrastructures.
Comment anonymously. Login not required.
