Pfsense invalid chains after Y generation cert

Unless I did something wrong, concatenating [domain].cer followed by ca.cer is the same as the fullchain and ca.cer.

Maybe if this was working correctly, you would be correct and the YE2 certificate would be there twice?

domain.cer for reference:

-----BEGIN CERTIFICATE-----
MIIDrjCCAzWgAwIBAgISBUMHGH725Ku6/kilkTrPVd/7MAoGCCqGSM49BAMDMDMx
CzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQwwCgYDVQQDEwNZ
RTIwHhcNMjYwNTMxMTQzNDQxWhcNMjYwODI5MTQzNDQwWjAdMRswGQYDVQQDDBIq
LmNpcmN1aXRicmljay5uZXQwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAT9pbt3U2EH
DSJzI+H8NgUPJAkQ0PLE5Oku0lZ3WkfSN+aM2U1qUoL95m3zRQFLQt7ZHMZUVIjm
85meYdGoZMxHPSK6peeNy5wQ8jEFKORa3EknECtC/GfNc49itfy2TFajggIgMIIC
HDAOBgNVHQ8BAf8EBAMCB4AwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDAYDVR0TAQH/
BAIwADAdBgNVHQ4EFgQU40r3Xa/4Zf4p0b9g5vKE5wSP8ygwHwYDVR0jBBgwFoAU
uVnyjs8i8IbTN0j/dhQYuoLYVYcwMwYIKwYBBQUHAQEEJzAlMCMGCCsGAQUFBzAC
hhdodHRwOi8veWUyLmkubGVuY3Iub3JnLzAdBgNVHREEFjAUghIqLmNpcmN1aXRi
cmljay5uZXQwEwYDVR0gBAwwCjAIBgZngQwBAgEwLwYDVR0fBCgwJjAkoCKgIIYe
aHR0cDovL3llMi5jLmxlbmNyLm9yZy8xMjQuY3JsMIIBCwYKKwYBBAHWeQIEAgSB
/ASB+QD3AHYAyKPEf8ezrbk1awE/anoSbeM6TkOlxkb5l605dZkdz5oAAAGefqrt
TAAABAMARzBFAiEAkLCTiVq8tNl5NS4Q8Mi4Onc1cPp+Rcy2ZujvpexrUiACIFeQ
YXX8Aa64c/LkTqDWGtgdRArWbkJxihddeKtbjLMdAH0AJuNkblhpISO8ND9HJDWb
N5LNJFqI2BXTkzP9mRirRyMAAAGefqrszQAIAAAFABdyprsEAwBGMEQCIFKrVjSF
th/fiWVGnOBiVlY4JsdFWWjiqJrtoDNwkSdyAiAuQlld4jdpER89iwwmSsTm8We0
PfqBEq1NENUGYxneWzAKBggqhkjOPQQDAwNnADBkAjBgZdYcB0Nu+grCuZD81H5R
pzBkFi+TJzaxLhhvUrNrD4iRyhc9dkZmwVP5n3Yss4ICMDJrA6dRhsCpz8SGRqM9
/NBrA4sl+MYF9+xgzWthW8lTWakb0m9FbWyP1ZBTJaeIaA==
-----END CERTIFICATE-----

If there is another package modifying the file on the pfsense side that would be good to know, nothing I have setup should be doing that.

If this is truly a pfsense issue, I can post on their forum to get a conversation going.

No unless [domain].cer and fullchain.cer have the same content. But they don't based on what you've sent us.

Based on this:

I thought you literally concatenated the two files (e.g., cat [file a] [file b] >[new file]).

But if you did it in some other way and skipped that cert that exists in both files then yeah it's fine.

The certificate you posted is a YE2 certificate issued on May 31. However, an external OpenSSL test currently sees an E8 certificate issued on May 1. Where is the May 31 certificate installed, and what device is actually serving HTTPS for books.circuitbrick.net?

I don't normally do this manually so I missed moving the new test fullchain I made before running Ansible. An openssl test should show the YE2 cert now

Yes, it does.

Yeah, as @dextercd noted the fullchain file should be just as the ACME Client received it from Let's Encrypt. It then has to split it to make your leaf cert and ca.cer. Usually these days servers use the fullchain but some do not which is why ACME Clients often offer variations.

It seems unlikely that acme.sh itself would damage fullchain in the process. It's possible I suppose but I think it more likely something that ran after that damaged fullchain. Something like an import or transfer to some server or system.

We know LE wasn't at fault otherwise the ca.cer file wouldn't have the correct chain in it. That is, if LE sent you the wrong thing the ca.cer would be wrong too.

As for why is this a problem now .... prior to the Y generation certs the default chain for ECDSA certs just had the one intermediate to ISRG Root X1 which is LE's most commonly trusted root.

With the Y generation there are now 3 intermediates. And, the first one does not link to a widely trusted root (it is for the new Y root). You need the other two intermediates to link to more trusted roots of ISRG Root X1 and ISRG Root X2.

So, it is likely the culprit has been "truncating" fullchain for a long time. It just didn't matter before because there was only the one intermediate until Y.

I think the certificate itself may be a distraction at this point.

The original OpenSSL test was seeing the older E8 certificate. After you moved the new fullchain and reran things, OpenSSL started seeing the YE2 certificate. So that part seems explained.

What I'm trying to understand now is the workflow.

How is the certificate getting from pfSense to books.circuitbrick.net?

Is Ansible deploying directly to the web server, or is there a reverse proxy, HAProxy, load balancer, etc. somewhere in the middle?

I only ask because my own deployment does some post-processing before Apache ever sees the certificate, so I tend to follow the certificate all the way to the device actually serving port 443.

That may tell us more than SSL Labs at this point.

Your idea sounds likely that maybe this has been broken for a long time and just now it's an issue.

The workflow is:

pfsense runs the ACME package to get the cert and then pfsense runs a local script to copy the fullchain and key to my reverse proxy and that same script also does an API call to run an Ansible playbook in my Semaphore server which copies the cert from the reverse proxy to my public servers and reloads their given services.

However this is just a straight copy, I'm not doing anything to the files, though given what I found in this discussion, an easy fix at this point is probably to just update my script to concatenate it beforehand.

Now that we understand the workflow, I think we're finally looking in the right place.

One thing I've found useful over the years is treating the ACME output as source material rather than immediately deploying it everywhere.

In my environment I do some validation and processing first, then deploy the resulting fullchain and key to the systems that need them. That way I'm working with the same known-good files everywhere downstream.

Given the number of moving parts in your workflow (pfSense, reverse proxy, Semaphore, Ansible, public servers), I'd probably be inclined to verify exactly what file exists at each step before changing the deployment logic. It may help narrow down where things started diverging.

If it helps, I'm happy to compare notes off-thread on deployment workflows.

Have you updated to the Y series yet? Because using riptidetech.io I see the prior generation

Good catch.

riptidetech.io is still on the E-series chain.

The testing I've been referring to was done against yachats-gardens.com after moving to the Y-series hierarchy. That's actually what prompted me to revisit my deployment script and verification logic in the first place.

Even there, the server ends up presenting the leaf certificate and intermediate only. The additional trust-path complexity is handled during validation, not by serving additional certificates to clients.

What's that? :slight_smile:

SSL Labs shows a failing validation: SSL Server Test: yachats-gardens.com (Powered by Qualys SSL Labs)

Seems like you can reproduce the problem. That's good news.

I can also reproduce that domain failing using openssl. It fails because I don't have the root that YE1 links to

openssl s_client -connect yachats-gardens.com:443 -verify_hostname yachats-gardens.com

Connecting to 192.173.156.118
depth=1 C=US, O=Let's Encrypt, CN=YE1
verify error:num=20:unable to get local issuer certificate
verify return:1
depth=0 
verify return:1
DONE

Fair point. I can reproduce the SSL Labs result.

What has me hesitating is that Hardenize and OpenSSL both validate the chain successfully on my end, and browsers don't appear to have any issue with it either.

So I've definitely reproduced the SSL Labs symptom. I'm still trying to determine whether that's exposing a genuine deployment problem or a difference in how SSL Labs is building and evaluating the trust path.

Did you see my update to my post showing openssl failing? What is your openssl command?

curl fails too

curl -i https://yachats-gardens.com 

curl: (60) SSL certificate OpenSSL verify result: 
unable to get local issuer certificate (20)
More details here: https://curl.se/docs/sslcerts.html

I probably didn't make it clear earlier (I'm not the best at explaining things), but checking the fullchain on pfsense before it's copied, it does not have have all of the intermediates, it only has one.

This is again why I don't feel like this is an issue with my multiple hops to my web servers with a script and Ansible in between. I could be wrong, but I am not finding anything so far that eludes to that being my issue.

Here is the output from that fullchain cert on pfsense:

-----BEGIN CERTIFICATE-----
MIIDrjCCAzWgAwIBAgISBUMHGH725Ku6/kilkTrPVd/7MAoGCCqGSM49BAMDMDMx
CzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQwwCgYDVQQDEwNZ
RTIwHhcNMjYwNTMxMTQzNDQxWhcNMjYwODI5MTQzNDQwWjAdMRswGQYDVQQDDBIq
LmNpcmN1aXRicmljay5uZXQwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAT9pbt3U2EH
DSJzI+H8NgUPJAkQ0PLE5Oku0lZ3WkfSN+aM2U1qUoL95m3zRQFLQt7ZHMZUVIjm
85meYdGoZMxHPSK6peeNy5wQ8jEFKORa3EknECtC/GfNc49itfy2TFajggIgMIIC
HDAOBgNVHQ8BAf8EBAMCB4AwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDAYDVR0TAQH/
BAIwADAdBgNVHQ4EFgQU40r3Xa/4Zf4p0b9g5vKE5wSP8ygwHwYDVR0jBBgwFoAU
uVnyjs8i8IbTN0j/dhQYuoLYVYcwMwYIKwYBBQUHAQEEJzAlMCMGCCsGAQUFBzAC
hhdodHRwOi8veWUyLmkubGVuY3Iub3JnLzAdBgNVHREEFjAUghIqLmNpcmN1aXRi
cmljay5uZXQwEwYDVR0gBAwwCjAIBgZngQwBAgEwLwYDVR0fBCgwJjAkoCKgIIYe
aHR0cDovL3llMi5jLmxlbmNyLm9yZy8xMjQuY3JsMIIBCwYKKwYBBAHWeQIEAgSB
/ASB+QD3AHYAyKPEf8ezrbk1awE/anoSbeM6TkOlxkb5l605dZkdz5oAAAGefqrt
TAAABAMARzBFAiEAkLCTiVq8tNl5NS4Q8Mi4Onc1cPp+Rcy2ZujvpexrUiACIFeQ
YXX8Aa64c/LkTqDWGtgdRArWbkJxihddeKtbjLMdAH0AJuNkblhpISO8ND9HJDWb
N5LNJFqI2BXTkzP9mRirRyMAAAGefqrszQAIAAAFABdyprsEAwBGMEQCIFKrVjSF
th/fiWVGnOBiVlY4JsdFWWjiqJrtoDNwkSdyAiAuQlld4jdpER89iwwmSsTm8We0
PfqBEq1NENUGYxneWzAKBggqhkjOPQQDAwNnADBkAjBgZdYcB0Nu+grCuZD81H5R
pzBkFi+TJzaxLhhvUrNrD4iRyhc9dkZmwVP5n3Yss4ICMDJrA6dRhsCpz8SGRqM9
/NBrA4sl+MYF9+xgzWthW8lTWakb0m9FbWyP1ZBTJaeIaA==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIICjDCCAhGgAwIBAgIQTfOxXdbAeExQfNN7WObxFTAKBggqhkjOPQQDAzAuMQsw
CQYDVQQGEwJVUzENMAsGA1UEChMESVNSRzEQMA4GA1UEAxMHUm9vdCBZRTAeFw0y
NTA5MDMwMDAwMDBaFw0yODA5MDIyMzU5NTlaMDMxCzAJBgNVBAYTAlVTMRYwFAYD
VQQKEw1MZXQncyBFbmNyeXB0MQwwCgYDVQQDEwNZRTIwdjAQBgcqhkjOPQIBBgUr
gQQAIgNiAARxmrQzkdbEEL3MqXt3dJQttYc47axkdDTHud5TPqM2z5uSD5cmk0Wr
HlWXvnlvqBLqiB34kluxIbmMyAiq3/YD6e80/vV259K8XQIdjFXloYOa0mIU71f7
HQ09PvYDlw+jge4wgeswDgYDVR0PAQH/BAQDAgGGMBMGA1UdJQQMMAoGCCsGAQUF
BwMBMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFLlZ8o7PIvCG0zdI/3YU
GLqC2FWHMB8GA1UdIwQYMBaAFKPIJlqOoUzQNWP8myPIOq5W809WMDIGCCsGAQUF
BwEBBCYwJDAiBggrBgEFBQcwAoYWaHR0cDovL3llLmkubGVuY3Iub3JnLzATBgNV
HSAEDDAKMAgGBmeBDAECATAnBgNVHR8EIDAeMBygGqAYhhZodHRwOi8veWUuYy5s
ZW5jci5vcmcvMAoGCCqGSM49BAMDA2kAMGYCMQDIcnw5dcZLN9ffynXnnkLD/itS
JEycJPb3sRkzeqBowup7vOsAwaqoCnNn/jh9wycCMQCJM6CPlaOC4pQYYbJtVPYb
DKrIb2EKk5NpOpE6/XttQYZV/3gilB9l+Cc/DOVwmyg=
-----END CERTIFICATE-----

Intermediate certificate required. Unable to get issuer certificate.

  1. Subject CN: *.circuitbrick.net > Issuer CN: YE2
  2. Subject CN: YE2 > Issuer CN: Root YE

I am interested to hear that it sounds like I am not unique in replicating this now?

No does not look like anything you are doing wrong. Something is truncating the normal fullchain and rip sees this too. More research being done offline

Unexpectedly, combining the leaf and ca.cer makes a proper fullchain so that is viable work-around until culprit found. I say unexpectedly b/c fullchain is usually that same combination.

Earlier when @MikeMcQ mentioned that I was able to fully reproduce OP's problem, I realized that I also had the same problem. So I spent over 12 hours yesterday and 3 hours this morning confirming the problem and fixing it.

One thing that became apparent during my testing is that there is a distinction between the original acme.sh working directory and the certificate files exported by pfSense.

Most operators naturally work from /conf/acme/ because it is persistent and survives reboots. However, the original certificate artifacts generated by acme.sh are stored in:

/tmp/acme/<domain>/<domain>/

In my case, the difference was significant:

grep -c "BEGIN CERTIFICATE" \
/tmp/acme/yachats-gardens.com/yachats-gardens.com/fullchain.cer

returned:

4

while:

grep -c "BEGIN CERTIFICATE" \
/cf/conf/acme/yachats-gardens.com.fullchain

returned:

2

My deployment script was pulling certificates from /cf/conf/acme/, so it was faithfully deploying the 2-certificate chain found there. The deployment process on the destination server was working correctly; it was simply being given a different file than the one originally produced by acme.sh.

The solution was to use the original fullchain.cer from the acme.sh working directory and deploy that file unchanged. Once I did that, the complete 4-certificate chain was presented correctly, OpenSSL validation succeeded, and SSL Labs reported an A+ configuration.

The key is that if you're troubleshooting chain-related issues, verify the contents of the original acme.sh files in /tmp/acme/... and compare them to the exported files you're actually deploying. In my case, the difference between those two locations explained the entire issue.

Examples:

OpenSSL s_client output from external host (ns1)
root@ns1:~) ~>> openssl s_client -connect yachats-gardens.com:443 -servername yachats-gardens.com -showcerts </dev/null
CONNECTED(00000003)
depth=3 C = US, O = Internet Security Research Group, CN = ISRG Root X2
verify return:1
depth=2 C = US, O = ISRG, CN = Root YE
verify return:1
depth=1 C = US, O = Let's Encrypt, CN = YE2
verify return:1
depth=0
verify return:1
---
Certificate chain
0 s:
i:C = US, O = Let's Encrypt, CN = YE2
a:PKEY: id-ecPublicKey, 256 (bit); sigalg: ecdsa-with-SHA384
v:NotBefore: May 30 20:37:52 2026 GMT; NotAfter: Jun  6 12:37:51 2026 GMT
-----BEGIN CERTIFICATE-----
MIIDcTCCAvigAwIBAgISBiFUF8KvKeSLzPh8K2W/IzVwMAoGCCqGSM49BAMDMDMx
CzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQwwCgYDVQQDEwNZ
RTIwHhcNMjYwNTMwMjAzNzUyWhcNMjYwNjA2MTIzNzUxWjAAMFkwEwYHKoZIzj0C
AQYIKoZIzj0DAQcDQgAE5BJhuKFILi5nvACDr0io88fgBdTXjKRUP22WjKBzvA8M
JI3I+eqJkpjJyX16sipjPz2Js/Tdr4AwWQUUQSwBlKOCAh0wggIZMA4GA1UdDwEB
/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDATAMBgNVHRMBAf8EAjAAMB8GA1Ud
IwQYMBaAFLlZ8o7PIvCG0zdI/3YUGLqC2FWHMDMGCCsGAQUFBwEBBCcwJTAjBggr
BgEFBQcwAoYXaHR0cDovL3llMi5pLmxlbmNyLm9yZy8wOAYDVR0RAQH/BC4wLIIV
Ki55YWNoYXRzLWdhcmRlbnMuY29tghN5YWNoYXRzLWdhcmRlbnMuY29tMBMGA1Ud
IAQMMAowCAYGZ4EMAQIBMC8GA1UdHwQoMCYwJKAioCCGHmh0dHA6Ly95ZTIuYy5s
ZW5jci5vcmcvMTEzLmNybDCCAQwGCisGAQQB1nkCBAIEgf0EgfoA+AB2AJaXZL9V
WJet90OHaDcIQnfp8DrV9qTzNm5GpD8PyqnGAAABnnrREyIAAAQDAEcwRQIgI9ei
gA8DG+VkM+XW3eHFe++DvVXb86J8AxSC+pUtTC0CIQCEPm0aiSgWxbht9yItNYHU
UBgjNHo00/sxa6ExFVjjYQB+AH89N+f4kj2OcWW+sNPqvucqIr5GwMuExBbU5LmC
ZMvCAAABnnrREooACAAABQBWt7h/BAMARzBFAiEAjozRI3+ZkldlZyMUh+5nd1Zs
ONvUN7HtFlyUxgRGkQoCIHa+20sKVWlOAQE+g6V6wUOQOVXa4Bi6WEHtGNem5P3X
MAoGCCqGSM49BAMDA2cAMGQCMQCdhRX7+7JDDECK/UNF7E519Ln4Y9yRQz2q1/ov
7zqrcadz6Av19qJeESWmEMV4l/ECLzkjFnDACKh+eZPQS8FujsUpvveTRF9pTa00
uE8iyyMl3GuY/dKsnf0pMG5sn+ew
-----END CERTIFICATE-----
1 s:C = US, O = Let's Encrypt, CN = YE2
i:C = US, O = ISRG, CN = Root YE
a:PKEY: id-ecPublicKey, 384 (bit); sigalg: ecdsa-with-SHA384
v:NotBefore: Sep  3 00:00:00 2025 GMT; NotAfter: Sep  2 23:59:59 2028 GMT
-----BEGIN CERTIFICATE-----
MIICjDCCAhGgAwIBAgIQTfOxXdbAeExQfNN7WObxFTAKBggqhkjOPQQDAzAuMQsw
CQYDVQQGEwJVUzENMAsGA1UEChMESVNSRzEQMA4GA1UEAxMHUm9vdCBZRTAeFw0y
NTA5MDMwMDAwMDBaFw0yODA5MDIyMzU5NTlaMDMxCzAJBgNVBAYTAlVTMRYwFAYD
VQQKEw1MZXQncyBFbmNyeXB0MQwwCgYDVQQDEwNZRTIwdjAQBgcqhkjOPQIBBgUr
gQQAIgNiAARxmrQzkdbEEL3MqXt3dJQttYc47axkdDTHud5TPqM2z5uSD5cmk0Wr
HlWXvnlvqBLqiB34kluxIbmMyAiq3/YD6e80/vV259K8XQIdjFXloYOa0mIU71f7
HQ09PvYDlw+jge4wgeswDgYDVR0PAQH/BAQDAgGGMBMGA1UdJQQMMAoGCCsGAQUF
BwMBMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFLlZ8o7PIvCG0zdI/3YU
GLqC2FWHMB8GA1UdIwQYMBaAFKPIJlqOoUzQNWP8myPIOq5W809WMDIGCCsGAQUF
BwEBBCYwJDAiBggrBgEFBQcwAoYWaHR0cDovL3llLmkubGVuY3Iub3JnLzATBgNV
HSAEDDAKMAgGBmeBDAECATAnBgNVHR8EIDAeMBygGqAYhhZodHRwOi8veWUuYy5s
ZW5jci5vcmcvMAoGCCqGSM49BAMDA2kAMGYCMQDIcnw5dcZLN9ffynXnnkLD/itS
JEycJPb3sRkzeqBowup7vOsAwaqoCnNn/jh9wycCMQCJM6CPlaOC4pQYYbJtVPYb
DKrIb2EKk5NpOpE6/XttQYZV/3gilB9l+Cc/DOVwmyg=
-----END CERTIFICATE-----
2 s:C = US, O = ISRG, CN = Root YE
i:C = US, O = Internet Security Research Group, CN = ISRG Root X2
a:PKEY: id-ecPublicKey, 384 (bit); sigalg: ecdsa-with-SHA384
v:NotBefore: May 13 00:00:00 2026 GMT; NotAfter: Sep  2 23:59:59 2032 GMT
-----BEGIN CERTIFICATE-----
MIICpjCCAiugAwIBAgIRAIchZfw0tuX7qK3Vs3BftTowCgYIKoZIzj0EAwMwTzEL
MAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2VhcmNo
IEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDIwHhcNMjYwNTEzMDAwMDAwWhcN
MzIwOTAyMjM1OTU5WjAuMQswCQYDVQQGEwJVUzENMAsGA1UEChMESVNSRzEQMA4G
A1UEAxMHUm9vdCBZRTB2MBAGByqGSM49AgEGBSuBBAAiA2IABDwS/6vhrcVqcbBo
+wgdI3fwn9x7DNJJOY/lTOti0vkwuRN87RhEhTH17E7XyFjWsPYhIPt/wzOqxTd2
b+4ZJNy9ID04YywF9U5zasDVyGSNErVNtz8uSGh5izW87j77GaOB6zCB6DAOBgNV
HQ8BAf8EBAMCAQYwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDwYDVR0TAQH/BAUwAwEB
/zAdBgNVHQ4EFgQUo8gmWo6hTNA1Y/ybI8g6rlbzT1YwHwYDVR0jBBgwFoAUfEKW
rt5LSDv6kviejM9ti6lyN5UwMgYIKwYBBQUHAQEEJjAkMCIGCCsGAQUFBzAChhZo
dHRwOi8veDIuaS5sZW5jci5vcmcvMBMGA1UdIAQMMAowCAYGZ4EMAQIBMCcGA1Ud
HwQgMB4wHKAaoBiGFmh0dHA6Ly94Mi5jLmxlbmNyLm9yZy8wCgYIKoZIzj0EAwMD
aQAwZgIxAMU19WCtmxVND8UHBZRoma49Z7jPs64Dma0eTu1OChVbB/2J7GV3nvYK
Ax54uk1G9QIxAO0miLVJu8PLNiXXXkiE/gsK3CTRTF/aeo4bMX42Zw40csRU6AC2
6hSW1/IWaas6dg==
-----END CERTIFICATE-----
3 s:C = US, O = Internet Security Research Group, CN = ISRG Root X2
i:C = US, O = Internet Security Research Group, CN = ISRG Root X1
a:PKEY: id-ecPublicKey, 384 (bit); sigalg: RSA-SHA256
v:NotBefore: May 13 00:00:00 2026 GMT; NotAfter: Sep  2 23:59:59 2032 GMT
-----BEGIN CERTIFICATE-----
MIIEcDCCAligAwIBAgIQbI8dxyfHEX97r4U6yYD5zTANBgkqhkiG9w0BAQsFADBP
MQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFy
Y2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMTAeFw0yNjA1MTMwMDAwMDBa
Fw0zMjA5MDIyMzU5NTlaME8xCzAJBgNVBAYTAlVTMSkwJwYDVQQKEyBJbnRlcm5l
dCBTZWN1cml0eSBSZXNlYXJjaCBHcm91cDEVMBMGA1UEAxMMSVNSRyBSb290IFgy
MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEzZvVn4CDCuwJSvMWSj5cz3es3mcFDR0H
ttwW+1qLFNvicWDEukWVEYmO6gbf9yoWHKS5xcUy4APgHoIYOIvXRdgKam7mAHf7
AlF9ItgKbppbd9/w+kHsOdx1ymgHDB/qo4H1MIHyMA4GA1UdDwEB/wQEAwIBBjAd
BgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDwYDVR0TAQH/BAUwAwEB/zAd
BgNVHQ4EFgQUfEKWrt5LSDv6kviejM9ti6lyN5UwHwYDVR0jBBgwFoAUebRZ5nu2
5eQBc4AIiMgaWPbpm24wMgYIKwYBBQUHAQEEJjAkMCIGCCsGAQUFBzAChhZodHRw
Oi8veDEuaS5sZW5jci5vcmcvMBMGA1UdIAQMMAowCAYGZ4EMAQIBMCcGA1UdHwQg
MB4wHKAaoBiGFmh0dHA6Ly94MS5jLmxlbmNyLm9yZy8wDQYJKoZIhvcNAQELBQAD
ggIBAD2/e9frmMxNpCV03qUHegg+MV2wz9644YoXdqtH8RyWYcBO7xfjjGEXdU1e
/o0OkEFiynUCOSIk/vLLo7ttz6CPAeNlWfC0XNkoGeWgK6jjXvozBaGuGH5n0Ufo
shMeWTuURqNN5G00sSXDTBrpp2+mgvdZQjb8K11TYMA25QA+YHNfbIEL0BniAhKS
2gsnJjSzrdZLI+EZ7SEyqdR2rkjd1KutLDU+n3TFyxjniZVGur4YlhMP3mY/dV95
IruAkkjOZier6hGBdEgZXXvaCz9u9iVEadsIE75pAGL8oHV5vxdARDiotRpul1IN
/UZwzAbrfUFcw1HkAcYD/mlZfnQ2ieCF2MS7j3Vhv7JPDKp45fmykmzYNSrumRW0
upFFKDBOoF7hsOb7oLyHS+Uft6jOUfOrogj8YUx38hKb2K20r42OgsSdDdxdeYWc
MS3Sb6mwJeSZEYxJ2gaXnDSPaKhhrNkYwljyVQyr4Nq+MEJytXNTnHqaAcrNwZlV
pcJL1KBnMrMjP7eanvUwL3FYj3cF17jtboLt7gLoi4+2rWZFvn+w54jmd/FIuhhZ
cEaU/wvU6BUNMtcVquVGHp7itQeDth5j+XL3j4WJ2SABwzUl6OeYdgpIt/ITZa+p
TT0mQ/r5XyA4MEAiabn7XJjvCERlF2dcn2wqJw+CreTkkQ2R
-----END CERTIFICATE-----
---
Server certificate
subject=
issuer=C = US, O = Let's Encrypt, CN = YE2
---
No client certificate CA names sent
Peer signing digest: SHA256
Peer signature type: ECDSA
Server Temp Key: X25519, 253 bits
---
SSL handshake has read 3753 bytes and written 401 bytes
Verification: OK
---
New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
Server public key is 256 bit
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
Early data was not sent
Verify return code: 0 (ok)
---
DONE

</details>

Hopefully that helps narrow the troubleshooting path for the OP and anyone else experimenting with short-lived certificates.

Sorry you had to spend so much time on this, but thanks for investigating. I can confirm I see the same, the fullchain.cer shows all 4 for me as well in /tmp/acme/

So is the solution to just use the one in /tmp/acme/ or should I post in the pfsense forums letting them know there is a bug in the ACME package?

The short answer is Yes... just use the one in /tmp/acme/

At this point, I would hold off on calling it a bug until we understand the intent of the package maintainers.

What we've now confirmed is that acme.sh is obtaining the complete chain correctly. Both of us see four certificates present in:

/tmp/acme/<domain>/<domain>/fullchain.cer

while the exported file in /conf/acme/ contains only two.

In my environment, the solution was to use the original artifacts from the acme.sh working directory. Specifically, I deployed the original fullchain.cer and the matching <domain>.key from:

/tmp/acme/<domain>/<domain>/

without rebuilding or modifying the certificate chain.

Once I did that, the complete four-certificate chain was presented correctly, OpenSSL validation succeeded, and SSL Labs reported an A+ configuration.

The remaining question is whether the ACME package is intentionally exporting a shortened chain to /conf/acme/, or whether the original fullchain should be preserved during export. That is something the package maintainers would need to clarify.

For anyone troubleshooting similar issues, I would start by comparing the contents of the original acme.sh files in /tmp/acme/... with the files that are actually being deployed. In my case, that comparison immediately identified the source of the problem.

Hello, I found this thread after all of my android devices stopped working after the last certificate updates on 2-June. I too have pfsense and my fullchain files only have 2 of the 4 required certs installed. I'm going to preform the above workaround abd grab the ca+cert files from /tmp/acme/ as those appear to have all the needed certs in total.

The move to the YE/YE2 seems to have broken my acme scripts in my version of pfsense which i can not upgrade for reasons