TLS-ALPN-01
Defined by RFC 8737. The client proves control by answering a TLS handshake on port 443 with a special self-signed certificate, rather than by serving anything over HTTP.
Its appeal is operational: validation happens entirely within the TLS layer, so a host that terminates TLS but serves no plain HTTP at all can still be validated, and nothing needs to be routed on port 80.
How it works
- The client is given a random
tokenand computes the key authorization as usual. - It generates a self-signed certificate that contains:
- a single
dNSNameSAN equal to the identifier being validated, and - a critical
id-pe-acmeIdentifierextension (OID1.3.6.1.5.5.7.1.31) whose value isSHA256(keyAuthorization).
- a single
- It arranges for that certificate to be presented when a handshake arrives
with SNI set to the identifier and ALPN protocol
acme-tls/1. acme-proxyperforms that handshake and inspects the certificate it gets back.
The proof is verified without ever completing an application-layer exchange: the certificate presented during the handshake is the answer.
What acme-proxy checks
- Exactly one
dNSNameSAN, matching the identifier. Not “at least one”. - The
id-pe-acmeIdentifierextension is present and marked critical, as RFC 8737 requires. - Its value equals the expected digest, compared in constant time.
The presented certificate is otherwise untrusted by design — it is self-signed and issued by the entity being challenged, so there is no chain to validate.
Configuration
[challenge]
enabled = ["tls-alpn-01"]
[challenge.tls_alpn_01]
port = 443
Reference
port (Integer) — Default: 443 | Env: ACME_PROXY_CHALLENGE__TLS_ALPN_01__PORT
Port the validation handshake connects to. RFC 8737 fixes this at 443 for the public Internet; it is configurable here for internal deployments that cannot bind it.
Client support
Support is thinner than for the other two types:
- lego implements a responder (
--tls). This is what the project’s end-to-end suite uses. - certbot and acme.sh do not implement a
tls-alpn-01responder. Listing this type is harmless for them — they simply pick another enabled type — but it cannot be their only option. - Servers that manage their own certificates, such as Caddy and Traefik, generally do support it natively.
If tls-alpn-01 is the only entry in challenge.enabled, certbot and acme.sh
clients will not be able to complete an order at all.
Limitations
- No wildcards. Use
dns-01. - Requires inbound connectivity from
acme-proxyto the client onport, the same constraint ashttp-01. - The responder must serve the ACME certificate only for the
acme-tls/1ALPN protocol, and its normal certificate otherwise. Serving it unconditionally would break ordinary traffic to that host for the duration.
Troubleshooting
| Event | Meaning |
|---|---|
challenge_tls_alpn_01_loaded | The handshake completed and a certificate was obtained. |
challenge_tls_alpn_01_matched | The certificate carried the right digest. Validation passed. |
challenge_validation_failed | Handshake failed, or the certificate did not satisfy the checks above. |
A handshake that fails outright usually means the responder did not negotiate
acme-tls/1 — many TLS servers simply fall through to their normal certificate
when they do not recognise the ALPN protocol, and the certificate then has no
id-pe-acmeIdentifier extension to find.