AWS certificates
Every Nexgen V3 environment terminates HTTPS on its Application Load Balancer (ALB) with certificates from AWS Certificate Manager (ACM). ACM renews them on its own, but only while each certificate's DNS validation record still exists. This page explains how that works, lists the certificates, and gives the commands to check them and fix them.
How the certificates work
There are two layers of TLS in front of the apps:
- Cloudflare edge. Hosts that are proxied in Cloudflare (orange cloud) get a certificate from Cloudflare. That's what a browser sees for
app.nutritionistpro.com,trial.nutritionistpro.com, and the*.staging.axxya.comhosts. Cloudflare issues and renews these itself. - ALB origin. Behind Cloudflare, and for any host that is DNS only (grey cloud), the ALB presents an ACM certificate. Cloudflare connects to the ALB in Full (strict) mode, so the ALB's certificate must be valid even for proxied hosts.
These rules apply to the ACM certificates:
- All of them are in
us-east-1. No other region has any. - They are public, Amazon-issued, and DNS validated. ACM gives each domain on a certificate a CNAME record to publish. The record is the same for a given domain in every certificate in the account, so one record can serve more than one certificate.
- The certificates are attached to an environment's ALB through the
http.public.certificateslist incopilot/environments/<env>/manifest.ymlin the nexgen_v3 repository. Copilot doesn't create them; you request them in ACM and paste the ARN in. How a manifest change reaches the ALB depends on the environment. For staging, CI runscopilot env deploy --name stagingas soon as a change tocopilot/environments/staging/orcopilot/environments/addons/is merged tomain, before it deploys any service, so merging a staging certificate change applies it right away. For prod and test, CI never deploys the environment; the change takes effect only when you runcopilot env deploy --name prodorcopilot env deploy --name testby hand. - The ALB has one HTTPS listener with several certificates. It picks one by the hostname the client asks for (SNI). The certificate marked as the default answers when no other one matches.
- A TLS wildcard covers exactly one label.
*.staging.axxya.comcoversnexgen-v3.staging.axxya.combut notapi.nexgen-v3.staging.axxya.com, which is why theapi.hosts have certificates of their own.
How renewal works
A newly issued ACM certificate is valid for 198 days, and ACM starts renewing it 45 days before it expires. Certificates issued before the change to 198 days were valid for 395 days; ACM renews those 60 days before they expire, and the renewed certificate is valid for 198 days. For a DNS-validated certificate, it checks that each domain's validation CNAME still resolves, then issues the new certificate in place. The ARN doesn't change, and the ALB picks up the new certificate without a deploy.
If a CNAME is missing, the renewal status stays at PENDING_VALIDATION and AWS sends warning notices to the account owner 30, 15, seven, three, and one day before the certificate expires. ACM keeps checking until the certificate expires. Once it has expired, ACM can't renew it; you have to request a new certificate and swap the ARN in the manifest.
The validation records live in three places:
nutritionistpro.com: the Cloudflarenutritionistpro.comzone.axxya.com: the Cloudflareaxxya.comzone.nutritionistpro.net: the Route 53 hosted zonesstaging.nutritionistpro.net,prod.nutritionistpro.net, andtest.nutritionistpro.net.
The validation records must be DNS only (grey cloud) in Cloudflare. Don't delete them when you tidy DNS; they look like clutter, but every certificate renewal depends on them. The nutritionistpro.com records were lost when the zone moved from GoDaddy to Cloudflare in June 2026, and every nutritionistpro.com certificate stopped renewing until they were added back in October 2026.
Certificate inventory
The account has seven certificates as of October 25, 2026, all issued and all in use, grouped here by the ALB they are attached to. Certificate IDs are the last part of the ARN, arn:aws:acm:us-east-1:202077789656:certificate/<id>. Expiry dates are the current ones; each moves out 198 days when ACM renews it.
The prod ALB has three certificates:
285278c0-1cde-40fa-883d-9c492daa3efd:*.prod.nutritionistpro.net,prod.nutritionistpro.net, and theapi.{nexgen,trial,nutkat}-v3.prod.nutritionistpro.nethosts. This is the default certificate. It carries the backend traffic, including the Chargebee webhooks, which callapi.nexgen-v3.prod.nutritionistpro.netbecause the Cloudflare edge fornutritionistpro.comrefuses RSA handshakes. Expires February 20, 2027.1193fb26-88f8-49bb-8025-c087dd41d40f:*.nutritionistpro.comandnutritionistpro.com. It coversapp.,trial.,api.,dev., andmcp.nutritionistpro.com, so it is the most important certificate in the account.mcp.nutritionistpro.comis DNS only, so MCP clients see this certificate directly. Expires May 10, 2027.65fc3533-67a8-4391-8244-e4b1d55f7871:nexgen1.nutritionistpro.comandnexgentrial.nutritionistpro.com, the retired V2 hostnames. Cloudflare answers both with a redirect to V3 at the edge and never contacts the origin, so this certificate no longer matters. Expires May 10, 2027.
The staging ALB has three certificates:
29b0a744-c752-4f4a-aaf0-d84a72770b48:*.staging.nutritionistpro.net,staging.nutritionistpro.net, and theapi.*-v3.staging.nutritionistpro.nethosts. This is the default certificate. Expires February 20, 2027.a8ab82ff-719c-4577-8965-037424f98b39:*.staging.axxya.com, the staging frontends that Playwright and staff use. Expires April 29, 2027.0791efa8-0f63-488f-b90c-d95cc36e70d1:api.{nexgen,trial,nutkat}-v3.staging.axxya.com. Expires April 30, 2027.
The test ALB has one certificate:
e6234eb3-b1fe-43b4-bf34-80f4cbb83ae5:*.test.nutritionistpro.net. Expires February 11, 2027.
Retired hostnames and deleted certificates
The nutritionistpro.com hostnames under staging and prod were retired on October 25, 2026. None of them had a DNS record after the zone moved to Cloudflare, so nothing could reach them:
- Staging had
*-v3.staging.nutritionistpro.comaliases on all six services. The aliases came out of the service manifests, the hostname came out of the Migration Manager bucket's allowed origins, and the two certificates came out of the staging manifest. Staging is now served on*.staging.axxya.comand*.staging.nutritionistpro.netonly. - Prod had
nexgen-v3.prod.nutritionistpro.comandtrial-v3.prod.nutritionistpro.comon the two frontends. They came out the same way. The prod backends are onapi.*-v3.prod.nutritionistpro.net, never.com.
Once the environment deploys had detached them, these certificates were deleted on October 25, 2026:
4e8acf6b-7da7-43c1-8e61-35de5fb0cf12:*.staging.nutritionistpro.com.461ea954-5e41-41cb-a884-4a6411d0752a:api.{nexgen,trial,nutkat}-v3.staging.nutritionistpro.com.12c9fd22-420e-4eba-88d9-576a2780b868:*.prod.nutritionistpro.com.77b73c90-fc60-4771-ae04-7f94a3ac9ccc:*.test.nutritionistpro.netwith theapi.hosts, expired September 20, 2026, and removed fromcopilot/environments/test/manifest.ymlthe same day.
A fifth, 02ef0694-5f5a-4399-bb60-cbcf2847283f, an older copy of the nexgen1 and nexgentrial certificate that expired October 23, 2026, was already gone from the account by then.
Don't add a staging or prod nutritionistpro.com hostname back without a DNS record and a certificate for it. Copilot refuses to deploy a service whose alias isn't covered by one of the environment's certificates, so a new hostname always needs its certificate first.
What happens when a certificate expires
The effect depends on how the host reaches the ALB:
- A proxied host (orange cloud). Visitors still see Cloudflare's valid certificate, but Cloudflare refuses the expired origin certificate in Full (strict) mode and returns error 526. The site is down.
- A DNS-only host (grey cloud). Browsers and API clients connect to the ALB and get a certificate error.
mcp.nutritionistpro.comis in this group. - A host with no DNS record, or one Cloudflare redirects at the edge. Nothing happens. The certificate can expire, and you can remove it from the manifest when convenient.
Check the certificates
All the commands in this section are read-only. Run them from a machine whose AWS CLI points at account 202077789656.
List every certificate
aws acm list-certificates only returns RSA 2048 certificates unless you pass --includes, so always pass every key type. This prints each certificate with its status, expiry, and renewal status:
for arn in $(aws acm list-certificates --region us-east-1 --includes keyTypes=RSA_1024,RSA_2048,RSA_3072,RSA_4096,EC_prime256v1,EC_secp384r1,EC_secp521r1 --query 'CertificateSummaryList[].CertificateArn' --output text); do aws acm describe-certificate --region us-east-1 --certificate-arn "$arn" --query 'Certificate.[DomainName,Status,NotAfter,RenewalSummary.RenewalStatus,length(InUseBy)]' --output text; done
A healthy certificate shows ISSUED and a renewal status of SUCCESS or None. None is normal for a certificate that ACM hasn't started renewing yet. The last column is the number of load balancers using the certificate; 0 means nothing uses it. If a certificate in use shows PENDING_VALIDATION, follow Fix a renewal that is stuck.
To check every region, in case someone created a certificate outside us-east-1:
for r in $(aws ec2 describe-regions --query 'Regions[].RegionName' --output text); do echo "$r $(aws acm list-certificates --region $r --includes keyTypes=RSA_2048,EC_prime256v1 --query 'length(CertificateSummaryList)' --output text)"; done
Show one certificate's validation records
Replace <id> with the certificate ID:
aws acm describe-certificate --region us-east-1 --certificate-arn arn:aws:acm:us-east-1:202077789656:certificate/<id> --query 'Certificate.DomainValidationOptions[].[DomainName,ValidationStatus,ResourceRecord.Name,ResourceRecord.Value]' --output text
During a renewal, RenewalSummary.DomainValidationOptions shows the renewal's own per-domain status. Use it to see which domains ACM has already validated:
aws acm describe-certificate --region us-east-1 --certificate-arn arn:aws:acm:us-east-1:202077789656:certificate/<id> --query 'Certificate.RenewalSummary.[RenewalStatus,UpdatedAt,DomainValidationOptions[].[DomainName,ValidationStatus]]' --output json
Check that a validation record resolves
Ask the zone's own nameservers first, then a public resolver. Both should print the acm-validations.aws. value. Use anton.ns.cloudflare.com for the Cloudflare zones; for a Route 53 zone, use one of the nameservers that dig +short NS staging.nutritionistpro.net prints.
dig +short CNAME <ResourceRecord.Name> @anton.ns.cloudflare.com
dig +short CNAME <ResourceRecord.Name> @8.8.8.8
An empty answer means the record is missing. Add it as described in the next section.
Check what the load balancer actually serves
List the certificates on an environment's HTTPS listener. Replace <env> with staging, test, or prod:
LB=$(aws elbv2 describe-load-balancers --region us-east-1 --query 'LoadBalancers[].LoadBalancerArn' --output text | tr '\t' '\n' | while read a; do [ "$(aws elbv2 describe-tags --region us-east-1 --resource-arns $a --query "TagDescriptions[0].Tags[?Key=='copilot-environment'].Value" --output text)" = "<env>" ] && echo $a; done)
LISTENER=$(aws elbv2 describe-listeners --region us-east-1 --load-balancer-arn "$LB" --query 'Listeners[?Port==`443`].ListenerArn' --output text)
aws elbv2 describe-listener-certificates --region us-east-1 --listener-arn "$LISTENER" --output table
To see the certificate the ALB presents for one hostname, connect to the ALB directly and pass the hostname. This skips Cloudflare, so it works for proxied hosts too. Get the ALB's DNS name from aws elbv2 describe-load-balancers:
echo | openssl s_client -connect <ALB DNS name>:443 -servername app.nutritionistpro.com 2>/dev/null | openssl x509 -noout -subject -enddate
Fix a renewal that is stuck
A renewal sticks at PENDING_VALIDATION when one or more validation CNAMEs don't resolve. To fix it:
- Show the certificate's validation records with the command in Show one certificate's validation records. Note each
ResourceRecord.NameandResourceRecord.Value. - Check each record with
dig, as in Check that a validation record resolves. Skip any that already resolve. - Add each missing record in the zone that owns the domain. In Cloudflare, open the zone and go to DNS > Records > Add record. Choose type CNAME, paste the name without the zone suffix and without the trailing dot, paste the value, set Proxy status to DNS only, and click Save. In Route 53, create a CNAME record in the matching hosted zone.
- Run the
digcommands again. Both nameservers should now return the value. If the public resolver still shows nothing, wait a few minutes for caching and try again. - Wait for ACM. It rechecks pending renewals on its own schedule, usually within a few hours. Each domain's renewal status changes to
SUCCESSfirst; the certificate's renewal status changes toSUCCESSandNotAftermoves out 198 days once ACM issues the new certificate. You don't need to change or deploy anything.
If the certificate is in use and expires before ACM finishes, replace it as described in the next section.
Replace a certificate
Replace a certificate when it has expired, or when you need different hostnames on it. ACM certificates can't be edited; a change of hostnames always means a new certificate. These commands change shared state.
-
Request the certificate. List every name it must cover:
aws acm request-certificate --region us-east-1 --validation-method DNS --domain-name '*.nutritionistpro.com' --subject-alternative-names nutritionistpro.com --query CertificateArn --output textThe command prints the new ARN.
-
Show the new certificate's validation records, and add any that don't already resolve, as in Fix a renewal that is stuck. A domain that already has a record from an older certificate reuses it.
-
Wait until the status is
ISSUED. This usually takes a few minutes once the records resolve:aws acm wait certificate-validated --region us-east-1 --certificate-arn <new ARN> -
In the nexgen_v3 repository, edit
copilot/environments/<env>/manifest.yml. Add the new ARN underhttp.public.certificateswith a comment naming the hosts it covers, and remove the old ARN. Merge the change. -
Deploy the environment. This updates the ALB listener in place with no downtime. For staging, merging the change to
maindoes this for you; CI runscopilot env deploy --name stagingbefore it deploys any service. For prod or test, run this from the repository root after the merge, replacing<env>withprodortest:copilot env deploy --name <env> -
Check that the listener carries the new certificate and that the hostname serves it, with the commands in Check what the load balancer actually serves.
-
Delete the old certificate once nothing uses it. ACM refuses to delete a certificate that is still attached to a load balancer:
aws acm delete-certificate --region us-east-1 --certificate-arn <old ARN>
Add a hostname
Before you put a new hostname in a service manifest's http.alias, check that a certificate on that environment's ALB already covers it. A single-label name under an existing wildcard is covered; for example, newapp.nutritionistpro.com is covered by *.nutritionistpro.com. A name with an extra label, such as api.newapp.staging.axxya.com, isn't covered by any wildcard and needs a new certificate or a replacement with the extra name. Follow Replace a certificate.
A proxied axxya.com host also needs its own DNS record in Cloudflare before Cloudflare issues the edge certificate. A wildcard record alone isn't enough.
Incident history
October 2026: AWS emailed that *.staging.nutritionistpro.com, nexgen1.nutritionistpro.com, *.nutritionistpro.com, and *.prod.nutritionistpro.com could not be renewed automatically. All five nutritionistpro.com certificates were stuck at PENDING_VALIDATION because none of their validation CNAMEs existed in the Cloudflare zone. They were missed when the zone moved from GoDaddy to Cloudflare on June 27, 2026. The nutritionistpro.net and axxya.com certificates were unaffected. The eight records were added on October 24, 2026, and ACM validated every domain within hours. *.nutritionistpro.com, which was due to expire November 17, 2026, was the certificate at real risk; the two that expired on October 25, 2026, carried no traffic. The nexgen1 and *.nutritionistpro.com certificates both renewed to May 10, 2027. The other three were on retired hostnames and were deleted instead of renewed, along with an expired test certificate, on October 25, 2026, which left seven certificates in the account.