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.
Related: How to test Apache configuration
Related: How to enable or disable Apache modules
Related: How to manage the Apache web server service
$ sudo mkdir -p /etc/apache2/ssl
$ 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.
$ 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
$ 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/.
$ sudo vi /etc/apache2/sites-available/host.example.net.conf
Back up existing site configuration files before edits to avoid overwriting a working configuration.
<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>
$ sudo a2ensite host.example.net.conf Enabling site host.example.net. To activate the new configuration, you need to run: systemctl reload apache2
<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.
$ sudo apache2ctl configtest Syntax OK
Related: How to test Apache configuration
$ sudo systemctl restart apache2
On RHEL-based distributions the service is commonly named httpd.
$ 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.
$ 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.
$ 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 #####