Back to blog
AWSBest PracticesCloud SecurityMonitoring & LoggingOperations & Compliance

ACM Certificate Expiring Soon: Prevent TLS Outages on AWS

An expiring ACM certificate can break HTTPS and take your site down. Learn how to detect, renew, and automate AWS certificate renewal to avoid TLS outages.

TL;DR

This check flags ACM certificates that expire within 30 days. An expired TLS certificate breaks HTTPS on your load balancers, CloudFront distributions, and APIs, causing browser errors and outages. Fix it by enabling managed renewal (and DNS validation) so AWS rotates certificates automatically.

A certificate that lapses at 2am on a Saturday is one of those failures that turns a quiet weekend into a war room. The site goes down, customers see scary browser warnings, and the on-call engineer is left scrambling to figure out why traffic dropped to zero despite every server being healthy. AWS Certificate Manager (ACM) exists partly to prevent this, but it only protects you when it is configured correctly. This check catches the gap before it becomes an incident.


What this check detects

The acm_expiring check inspects every certificate in AWS Certificate Manager across your account and regions, and flags any whose NotAfter date falls within the next 30 days. A finding means a certificate is approaching expiry and has not yet been renewed.

There are two common reasons a certificate shows up here:

  • Manual or imported certificates that ACM does not renew on your behalf. These have to be replaced by hand.
  • ACM-issued certificates with broken managed renewal, usually because DNS validation records were removed or the certificate is no longer attached to an AWS resource.

Note: ACM automatically renews certificates it issued, but only when the certificate is in use by an integrated service (like an ELB, CloudFront, or API Gateway) and the original validation method still resolves. A certificate that is issued but unattached, or one validated by email, will not renew silently. That is exactly the scenario this check is designed to surface.


Why it matters

TLS certificates are the foundation of every HTTPS connection your users make. When one expires, the consequences are immediate and visible:

  • Hard outages. Browsers and clients refuse to establish a connection to an endpoint with an expired certificate. Your Application Load Balancer, CloudFront distribution, or API stops serving traffic over HTTPS.
  • Loss of customer trust. A full-page "Your connection is not private" warning is one of the most alarming things a user can see. Even a brief lapse erodes confidence and can spike support tickets.
  • Broken integrations. Machine-to-machine clients (mobile apps, partner APIs, webhooks) often fail closed on certificate errors with no retry. A payment processor calling your webhook will simply stop, and you may not notice until reconciliation.
  • Compliance gaps. Standards like PCI DSS require valid, properly managed TLS. An expired certificate on an in-scope endpoint is an audit finding.

The frustrating part is that certificate expiry is entirely predictable. Every certificate carries its own expiry date. There is no excuse for being surprised by it, yet it remains one of the most common causes of preventable downtime year after year.

Warning: Certificate expiry does not respect business hours. Certificates expire at the exact UTC timestamp in their NotAfter field, which is frequently overnight or on a weekend relative to your team. A 30-day warning window gives you room to act during working hours instead of firefighting at 3am.


How to fix it

The right fix depends on whether the certificate is ACM-issued or imported. Start by identifying the certificate and understanding its state.

Step 1: Find the expiring certificate

aws acm list-certificates \
  --certificate-statuses ISSUED \
  --query 'CertificateSummaryList[].{Domain:DomainName,Arn:CertificateArn}' \
  --output table

Then inspect the specific certificate to see its expiry date, renewal eligibility, and validation method:

aws acm describe-certificate \
  --certificate-arn arn:aws:acm:us-east-1:123456789012:certificate/abcd1234 \
  --query 'Certificate.{Domain:DomainName,NotAfter:NotAfter,Type:Type,RenewalEligibility:RenewalEligibility,ValidationMethod:DomainValidationOptions[0].ValidationMethod}'

The Type field is the key signal. AMAZON_ISSUED means ACM can manage renewal. IMPORTED means you own the renewal lifecycle entirely.

Step 2a: Fix an ACM-issued certificate

If the type is AMAZON_ISSUED but renewal has not happened, the usual culprit is validation. Check the renewal status:

aws acm describe-certificate \
  --certificate-arn arn:aws:acm:us-east-1:123456789012:certificate/abcd1234 \
  --query 'Certificate.DomainValidationOptions'

If the certificate uses email validation, switch it to DNS validation so renewal becomes automatic. ACM cannot change the validation method on an existing certificate, so request a new one with DNS validation:

aws acm request-certificate \
  --domain-name example.com \
  --subject-alternative-names "*.example.com" \
  --validation-method DNS \
  --query CertificateArn --output text

Then add the CNAME validation record ACM provides. If your domain is in Route 53, you can have ACM create the record for you in the console with a single click, or use the CNAME values from describe-certificate:

aws route53 change-resource-record-sets \
  --hosted-zone-id Z123456ABCDEFG \
  --change-batch '{
    "Changes": [{
      "Action": "UPSERT",
      "ResourceRecordSet": {
        "Name": "_abc123.example.com.",
        "Type": "CNAME",
        "TTL": 300,
        "ResourceRecords": [{ "Value": "_xyz789.acm-validations.aws." }]
      }
    }]
  }'

Tip: Once a DNS-validated certificate is attached to a resource, ACM begins managed renewal automatically and reissues roughly 60 days before expiry, with zero action from you. Leave the validation CNAME record in place permanently so future renewals can revalidate without manual steps. Deleting it is the single most common cause of a renewal that silently fails.

Step 2b: Replace an imported certificate

Imported certificates (often from an external CA) are never auto-renewed. You need to obtain a fresh certificate from your CA, then re-import it. Importing to the same ARN updates the certificate in place, which means attached resources pick up the new one without reconfiguration:

aws acm import-certificate \
  --certificate-arn arn:aws:acm:us-east-1:123456789012:certificate/abcd1234 \
  --certificate fileb://certificate.pem \
  --private-key fileb://private-key.pem \
  --certificate-chain fileb://chain.pem

Warning: If you request a brand new certificate ARN instead of re-importing to the existing one, you must update every resource that references the old ARN (load balancer listeners, CloudFront distributions, API custom domains). Missing one leaves part of your traffic on the soon-to-expire certificate. Re-importing in place avoids this entirely.

Step 3: Verify the resource is serving the new certificate

After renewal, confirm the live endpoint is actually presenting a valid certificate. Do not trust the console alone, check the wire:

echo | openssl s_client -servername example.com -connect example.com:443 2>/dev/null \
  | openssl x509 -noout -dates

You should see a notAfter date well into the future.


How to prevent it from happening again

The goal is to make certificate expiry impossible to miss, and ideally to make renewal something you never touch.

Prefer ACM-issued certificates with DNS validation

This is the highest-leverage change you can make. DNS-validated, ACM-issued certificates renew themselves indefinitely as long as the validation record exists and the certificate is in use. Migrate imported and email-validated certificates toward this model wherever your CA requirements allow.

Manage certificates as code

Defining certificates in Terraform or CloudFormation gives you a reviewable, repeatable source of truth and wires up validation automatically. Here is a Terraform example that requests a certificate, creates the Route 53 validation records, and waits for validation:

resource "aws_acm_certificate" "site" {
  domain_name               = "example.com"
  subject_alternative_names = ["*.example.com"]
  validation_method         = "DNS"

  lifecycle {
    create_before_destroy = true
  }
}

resource "aws_route53_record" "validation" {
  for_each = {
    for dvo in aws_acm_certificate.site.domain_validation_options : dvo.domain_name => {
      name   = dvo.resource_record_name
      type   = dvo.resource_record_type
      record = dvo.resource_record_value
    }
  }

  zone_id = var.hosted_zone_id
  name    = each.value.name
  type    = each.value.type
  records = [each.value.record]
  ttl     = 300
}

resource "aws_acm_certificate_validation" "site" {
  certificate_arn         = aws_acm_certificate.site.arn
  validation_record_fqdns = [for r in aws_route53_record.validation : r.fqdn]
}

Add expiry monitoring as a safety net

Even with managed renewal, monitor for expiry as defense in depth. ACM publishes the DaysToExpiry metric to CloudWatch. Alarm on it so you get a warning long before this check would even fire:

aws cloudwatch put-metric-alarm \
  --alarm-name acm-cert-expiry-example-com \
  --namespace AWS/CertificateManager \
  --metric-name DaysToExpiry \
  --dimensions Name=CertificateArn,Value=arn:aws:acm:us-east-1:123456789012:certificate/abcd1234 \
  --statistic Minimum \
  --period 86400 \
  --threshold 21 \
  --comparison-operator LessThanThreshold \
  --evaluation-periods 1 \
  --alarm-actions arn:aws:sns:us-east-1:123456789012:ops-alerts

Tip: AWS Config has a managed rule, acm-certificate-expiration-check, that evaluates all certificates against a configurable day threshold. Pair it with an automatic SNS notification or remediation, and you get account-wide coverage without writing a CloudWatch alarm per certificate.

Gate it in CI/CD

If you provision infrastructure through pipelines, run Lensix (or a policy-as-code tool) against your account on a schedule and fail the build when an expiring certificate is detected and not on track to renew. Catching the finding in a pipeline run turns a future outage into a tracked task today.


Best practices

  • Standardize on ACM-issued, DNS-validated certificates. They renew automatically and remove humans from the critical path.
  • Never delete validation CNAME records. They are required for ACM to revalidate during renewal. Treat them as permanent infrastructure.
  • Keep certificates attached to resources. ACM only auto-renews certificates that are in use by an integrated service. An orphaned certificate will quietly expire.
  • Re-import imported certificates to the same ARN so attached resources update without reconfiguration.
  • Watch the us-east-1 region for CloudFront. Certificates used by CloudFront must live in us-east-1, which teams running in other regions frequently overlook.
  • Cover wildcard and SAN coverage explicitly. A renewed certificate that drops a subject alternative name will break some hostnames while leaving others working, which is harder to diagnose than a clean outage.
  • Alert well ahead of expiry. A 30-day window is the floor, not the target. Aim to know weeks earlier so renewal happens calmly.

Certificate expiry is one of the rare outages that is fully foreseeable and fully preventable. Move your certificates to ACM-managed renewal with DNS validation, keep them attached and validated, and back it all with monitoring. Do that, and the acm_expiring finding becomes an early heads-up rather than the opening line of a postmortem.