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=host.example.net" -addext "subjectAltName=DNS:host.example.net,DNS:www.host.example.net" Generating a RSA private key ##### snipped ##### -----
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 Jan 10 13:43 /etc/apache2/ssl/apache.key
- Enable mod_ssl.
$ sudo a2enmod ssl 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/host.example.net.conf/.
$ sudo vi /etc/apache2/sites-available/host.example.net.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 host.example.net DocumentRoot /var/www/html SSLEngine on SSLCertificateFile /etc/apache2/ssl/apache.crt SSLCertificateKeyFile /etc/apache2/ssl/apache.key </VirtualHost>
- Enable the site configuration.
$ sudo a2ensite host.example.net.conf Enabling site host.example.net. 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 host.example.net Redirect permanent / https://host.example.net/ </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 Syntax OK
- 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 -skvI https://host.example.net/ * Host host.example.net:443 was resolved. * IPv6: (none) * IPv4: 192.0.2.40 * Trying 192.0.2.40:443... * Connected to host.example.net (192.0.2.40) port 443 * ALPN: curl offers h2,http/1.1 ##### snipped ##### * SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 / X25519 / RSASSA-PSS * Server certificate: * subject: CN=host.example.net * issuer: CN=host.example.net * SSL certificate verify result: self-signed certificate (18), continuing anyway. ##### snipped ##### > HEAD / HTTP/1.1 > Host: host.example.net ##### snipped ##### HTTP/1.1 200 OK Date: Sat, 10 Jan 2026 05:43:44 GMT Server: Apache/2.4.58 (Ubuntu) ##### 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/host.example.net-self-signed.crt $ sudo update-ca-certificates Updating certificates in /etc/ssl/certs... rehash: warning: skipping ca-certificates.crt,it does not contain exactly one certificate or CRL 1 added, 0 removed; done. Running hooks in /etc/ca-certificates/update.d... 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 -svI https://host.example.net/ * Host host.example.net:443 was resolved. * IPv6: (none) * IPv4: 192.0.2.40 * Trying 192.0.2.40:443... * Connected to host.example.net (192.0.2.40) port 443 * ALPN: curl offers h2,http/1.1 ##### snipped ##### * SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 / X25519 / RSASSA-PSS * Server certificate: * subject: CN=host.example.net * subjectAltName: host "host.example.net" matched cert's "host.example.net" * issuer: CN=host.example.net * SSL certificate verify ok. ##### snipped ##### < HTTP/1.1 200 OK < Date: Sat, 10 Jan 2026 05:43:44 GMT < Server: Apache/2.4.58 (Ubuntu) ##### 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.
