Even when free trusted certificates from Let’s Encrypt are available for public domains, self-signed certificates serve important roles: development environments, internal network services, IoT devices, automated testing pipelines, and machine-to-machine communication within a private network. Unlike Let’s Encrypt certificates, self-signed certificates require no domain ownership verification or outbound internet access — they can be generated in seconds for any hostname including localhost, private IP addresses, or internal DNS names. This guide covers generating self-signed certificates with OpenSSL on RHEL 9, creating a complete Subject Alternative Name (SAN) certificate, deploying to Nginx, trusting the certificate in the system CA store for curl/wget and browsers, and automating annual renewal with a systemd timer.
Prerequisites
- OpenSSL installed on RHEL 9 (included by default)
- Nginx or Apache for deployment examples
Step 1 — Verify OpenSSL is Available
openssl version -a
Step 2 — Generate a Self-Signed Certificate with SAN
Modern browsers reject certificates without Subject Alternative Names (SAN). Use an OpenSSL config file to include SANs:
# Create the config file
cat > /tmp/openssl-san.cnf <<'CNF'
[req]
default_bits = 4096
prompt = no
default_md = sha256
distinguished_name = dn
x509_extensions = v3_req
[dn]
C = US
ST = California
L = San Francisco
O = My Organisation
OU = IT
CN = internal.example.com
[v3_req]
subjectAltName = @alt_names
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
[alt_names]
DNS.1 = internal.example.com
DNS.2 = www.internal.example.com
DNS.3 = localhost
IP.1 = 192.168.1.10
IP.2 = 127.0.0.1
CNF
# Generate private key and certificate in one command
mkdir -p /etc/ssl/private
openssl req -x509 -nodes -days 365
-newkey rsa:4096
-keyout /etc/ssl/private/internal.example.com.key
-out /etc/ssl/certs/internal.example.com.crt
-config /tmp/openssl-san.cnf
chmod 600 /etc/ssl/private/internal.example.com.key
# Inspect the generated certificate
openssl x509 -in /etc/ssl/certs/internal.example.com.crt -text -noout | grep -A5 "Subject Alternative Name"
Step 3 — Deploy to Nginx
# /etc/nginx/conf.d/internal.example.com.conf
server {
listen 443 ssl http2;
server_name internal.example.com;
ssl_certificate /etc/ssl/certs/internal.example.com.crt;
ssl_certificate_key /etc/ssl/private/internal.example.com.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
root /var/www/internal;
index index.php index.html;
}
# Redirect HTTP to HTTPS
server {
listen 80;
server_name internal.example.com;
return 301 https://$host$request_uri;
}
nginx -t && systemctl reload nginx
Step 4 — Trust the Certificate in the System CA Store
# Copy cert to the system trust store
cp /etc/ssl/certs/internal.example.com.crt /etc/pki/ca-trust/source/anchors/
update-ca-trust
# Verify curl now trusts the cert (no -k flag)
curl https://internal.example.com/
Step 5 — Automate Annual Renewal
# /usr/local/bin/renew-selfsigned.sh
#!/bin/bash
openssl req -x509 -nodes -days 365
-newkey rsa:4096
-keyout /etc/ssl/private/internal.example.com.key
-out /etc/ssl/certs/internal.example.com.crt
-config /tmp/openssl-san.cnf
chmod 600 /etc/ssl/private/internal.example.com.key
cp /etc/ssl/certs/internal.example.com.crt /etc/pki/ca-trust/source/anchors/
update-ca-trust
systemctl reload nginx
# systemd timer for annual renewal (run 30 days before expiry)
chmod +x /usr/local/bin/renew-selfsigned.sh
# Add to crontab: run every year on Jan 1 at 02:00
echo "0 2 1 1 * root /usr/local/bin/renew-selfsigned.sh" > /etc/cron.d/renew-selfsigned
Conclusion
Self-signed certificates with SANs on RHEL 9 are suitable for internal services, development environments, and private networks where Let’s Encrypt cannot be used. Adding the certificate to the system CA trust store allows curl, wget, and other tools to trust it without the -k flag. For production public-facing websites, always use a trusted CA like Let’s Encrypt.
Next steps: How to Configure Nginx WebSocket Proxying on RHEL 9, How to Harden Nginx on RHEL 9, and How to Secure Apache with Let’s Encrypt and Certbot on RHEL 9.