This guide explains how to obtain and configure a Let's Encrypt TLS certificate for a bare IP address on Ubuntu 24.04 LTS.
As of January 2026, Let's Encrypt offers IP address certificates in general availability. These certificates:
- Are only available via the shortlived profile (~6-day validity)
- Require HTTP-01 or TLS-ALPN-01 validation (DNS-01 is not supported for IP addresses)
- Do not include CRL/OCSP URLs (per CA/Browser Forum Baseline Requirements for <10 day certs)
- Ubuntu 24.04 LTS server
- Public IPv4 or IPv6 address
- Port 80 accessible from the internet (for HTTP-01 challenge)
- Root or sudo access
apt update && apt install -y nginxVerify nginx is running and accessible on port 80:
curl -I http://YOUR_IP_ADDRESSAs of January 2026, certbot does not yet support IP address certificates. Use lego instead:
curl -sL https://github.com/go-acme/lego/releases/download/v4.31.0/lego_v4.31.0_linux_amd64.tar.gz \
| tar xz -C /usr/local/bin lego
# Verify installation
lego --versionCreate the lego directory and request the certificate:
mkdir -p /etc/lego
lego --accept-tos \
--http \
--http.webroot /var/www/html \
--domains YOUR_IP_ADDRESS \
--path /etc/lego \
--disable-cn \
run --profile shortlivedImportant flags:
| Flag | Purpose |
|---|---|
--accept-tos |
Accept Let's Encrypt terms of service |
--http |
Use HTTP-01 challenge |
--http.webroot /var/www/html |
Write challenge files to nginx's webroot |
--domains YOUR_IP_ADDRESS |
The IP address to certify |
--disable-cn |
Don't put IP in Common Name (required for IP certs) |
--profile shortlived |
Request shortlived profile (required for IP certs) |
On success, certificates are saved to /etc/lego/certificates/:
/etc/lego/certificates/YOUR_IP_ADDRESS.crt # Certificate + chain
/etc/lego/certificates/YOUR_IP_ADDRESS.key # Private key
/etc/lego/certificates/YOUR_IP_ADDRESS.issuer.crt # Issuer certificate
Edit /etc/nginx/sites-available/default:
# HTTP server - redirect to HTTPS (except ACME challenges)
server {
listen 80 default_server;
listen [::]:80 default_server;
# Allow ACME challenge requests for certificate renewal
location /.well-known/acme-challenge/ {
root /var/www/html;
}
# Redirect all other HTTP traffic to HTTPS
location / {
return 301 https://$host$request_uri;
}
}
# HTTPS server
server {
listen 443 ssl default_server;
listen [::]:443 ssl default_server;
# SSL certificate from Let's Encrypt (via lego)
ssl_certificate /etc/lego/certificates/YOUR_IP_ADDRESS.crt;
ssl_certificate_key /etc/lego/certificates/YOUR_IP_ADDRESS.key;
# Modern SSL configuration (TLS 1.2+)
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
# SSL session settings
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
root /var/www/html;
index index.html index.htm index.nginx-debian.html;
server_name _;
location / {
try_files $uri $uri/ =404;
}
}Remember to replace YOUR_IP_ADDRESS with your actual IP address.
Test and reload nginx:
nginx -t && systemctl reload nginxSince shortlived certificates are only valid for ~6 days, automatic renewal is critical.
Create /etc/cron.d/lego-renew:
# Renew Let's Encrypt IP certificate twice daily
# Renew when 2 days or less remain on certificate
15 3,15 * * * root /usr/local/bin/lego --accept-tos --http --http.webroot /var/www/html --domains YOUR_IP_ADDRESS --path /etc/lego --disable-cn renew --days 2 --profile shortlived --renew-hook "systemctl reload nginx" >> /var/log/lego-renew.log 2>&1Set correct permissions:
chmod 644 /etc/cron.d/lego-renewcurl -I http://YOUR_IP_ADDRESSExpected output includes:
HTTP/1.1 301 Moved Permanently
Location: https://YOUR_IP_ADDRESS/
curl -sv https://YOUR_IP_ADDRESS 2>&1 | grep -E "SSL connection|subject:|issuer:|expire"Expected output:
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
* subject: [NONE]
* issuer: C=US; O=Let's Encrypt; CN=YE2
* expire date: <approximately 6-7 days from now>
echo | openssl s_client -connect YOUR_IP_ADDRESS:443 2>/dev/null \
| openssl x509 -noout -dates -issuer -ext subjectAltNameExpected output:
notBefore=<issue date>
notAfter=<expiry date ~6-7 days later>
issuer=C = US, O = Let's Encrypt, CN = YE2
X509v3 Subject Alternative Name: critical
IP Address:YOUR_IP_ADDRESS
You forgot --disable-cn. IP addresses cannot be in the certificate's Common Name field, only in the Subject Alternative Name (SAN).
The shortlived profile is required for IP certificates. Make sure you're using --profile shortlived after the run or renew subcommand.
- Check that port 80 is accessible from the internet
- Verify the webroot path matches nginx's root directive
- Check logs:
cat /var/log/lego-renew.log - Test manually:
lego --accept-tos --http --http.webroot /var/www/html \ --domains YOUR_IP_ADDRESS --path /etc/lego --disable-cn \ renew --days 2 --profile shortlived
If the certificate expires before renewal, nginx won't start. Obtain a new certificate:
# Remove old certificate
rm /etc/lego/certificates/YOUR_IP_ADDRESS.*
# Temporarily configure nginx for HTTP only, then:
lego --accept-tos --http --http.webroot /var/www/html \
--domains YOUR_IP_ADDRESS --path /etc/lego --disable-cn \
run --profile shortlived
# Restore HTTPS config and reload
systemctl reload nginx-
Short validity period: These certificates expire in ~6 days. Ensure your renewal cron job is working reliably.
-
No OCSP/CRL: Short-lived certificates don't include revocation information. If compromised, you must wait for expiry or manually replace the certificate.
-
Rate limits: Let's Encrypt has rate limits. For IP certificates:
- 300 new orders per account per 3 hours
- Failed validations count against limits
-
IPv6 support: IPv6 addresses are also supported. Use the full IPv6 address format.
-
Why lego instead of certbot?: As of January 2026, certbot (v5.2.2) has client-side validation that rejects IP addresses. lego (v4.31.0+) fully supports IP certificates.