Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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

  1. The client is given a random token and computes the key authorization as usual.
  2. It generates a self-signed certificate that contains:
    • a single dNSName SAN equal to the identifier being validated, and
    • a critical id-pe-acmeIdentifier extension (OID 1.3.6.1.5.5.7.1.31) whose value is SHA256(keyAuthorization).
  3. It arranges for that certificate to be presented when a handshake arrives with SNI set to the identifier and ALPN protocol acme-tls/1.
  4. acme-proxy performs 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 dNSName SAN, matching the identifier. Not “at least one”.
  • The id-pe-acmeIdentifier extension 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-01 responder. 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-proxy to the client on port, the same constraint as http-01.
  • The responder must serve the ACME certificate only for the acme-tls/1 ALPN protocol, and its normal certificate otherwise. Serving it unconditionally would break ordinary traffic to that host for the duration.

Troubleshooting

EventMeaning
challenge_tls_alpn_01_loadedThe handshake completed and a certificate was obtained.
challenge_tls_alpn_01_matchedThe certificate carried the right digest. Validation passed.
challenge_validation_failedHandshake 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.