svn commit: r1935440 - in httpd/httpd/trunk: docs/manual/mod modules/md

[email protected] Tue, 16 Jun 2026 19:28:01 -0000
Newsgroups gmane.comp.apache.cvs
Message-ID <178163808115.2535667.4902214305010515287@svn03-he-fi>
Author: rjung
Date: Tue Jun 16 19:28:00 2026
New Revision: 1935440

Log:
mod_md: New directive: MDHttpProxyCACertificateFile
Sets the CA certificates to use for connections to the HTTPS proxy that has
been configured with MDHttpProxy.

Merge from icing/md:
https://github.com/icing/mod_md/commit/b7c956391aa116129e24a0191eec74616ff6b1a0

Modified:
   httpd/httpd/trunk/docs/manual/mod/mod_md.xml
   httpd/httpd/trunk/modules/md/md.h
   httpd/httpd/trunk/modules/md/md_acme.c
   httpd/httpd/trunk/modules/md/md_acme.h
   httpd/httpd/trunk/modules/md/md_acme_drive.c
   httpd/httpd/trunk/modules/md/md_core.c
   httpd/httpd/trunk/modules/md/md_curl.c
   httpd/httpd/trunk/modules/md/md_http.c
   httpd/httpd/trunk/modules/md/md_http.h
   httpd/httpd/trunk/modules/md/md_reg.c
   httpd/httpd/trunk/modules/md/md_reg.h
   httpd/httpd/trunk/modules/md/mod_md.c
   httpd/httpd/trunk/modules/md/mod_md_config.c
   httpd/httpd/trunk/modules/md/mod_md_config.h

Modified: httpd/httpd/trunk/docs/manual/mod/mod_md.xml
==============================================================================
--- httpd/httpd/trunk/docs/manual/mod/mod_md.xml	Tue Jun 16 19:17:22 2026	(r1935439)
+++ httpd/httpd/trunk/docs/manual/mod/mod_md.xml	Tue Jun 16 19:28:00 2026	(r1935440)
@@ -124,7 +124,7 @@ Protocols h2 http/1.1 acme-tls/1
         </highlight>
         <p>
             And the `tls-alpn-01` challenge type is available.
-	</p>
+        </p>
         </note>
 
         <note><title>Wildcard Certificates</title>
@@ -540,13 +540,42 @@ MDCertificateAuthority https://acme-stag
         </contextlist>
         <compatibility>Since version 2.4.69, a proxy can be configured separately for each MDomain.</compatibility>
         <usage>
-            <p>Use the given http forward proxy URL to connect to the <directive module="mod_md">MDCertificateAuthority</directive>.
+            <p>
+                Use the given http forward proxy URL to connect to the <directive module="mod_md">MDCertificateAuthority</directive>.
                 Define this if your webserver can only reach the internet with a forward proxy.
             </p>
         </usage>
     </directivesynopsis>
 
     <directivesynopsis>
+        <name>MDHttpProxyCACertificateFile</name>
+        <description>Sets the root (CA) certificates to use for TLS connections to the http-proxy.</description>
+        <syntax>MDHttpProxyCACertificateFile <var>path-to-pem-file</var></syntax>
+        <default>MDHttpProxyCACertificateFile none</default>
+        <contextlist>
+            <context>server config</context>
+        </contextlist>
+        <compatibility>Available in version 2.4.69 and later</compatibility>
+        <usage>
+            <p>
+                This is used for connections to the HTTPS forward proxy (<directive module="mod_md">MDHttpProxy</directive>).
+                It is needed if the certificate of the HTTPS proxy cannot be verified using the general CA root store.
+                This is sometimes the case in test setups or enterprise environments.
+            </p>
+            <p>
+                The certificate of the ACME server is verified with the root certificates set by
+                <directive module="mod_md">MDCACertificateFile</directive>, so you might need to use both settings.
+            </p>
+            <p>
+                Use "none" as path to disable explicitly.
+            </p>
+            <p>
+                This can be configured separately for each <directive module="mod_md">MDomain</directive>.
+            </p>
+        </usage>
+    </directivesynopsis>
+
+    <directivesynopsis>
         <name>MDMember</name>
         <description>Additional hostname for the managed domain.</description>
         <syntax>MDMember <var>hostname</var></syntax>
@@ -1623,6 +1652,7 @@ MDMessageCmd /etc/apache/md-message
         <contextlist>
             <context>server config</context>
         </contextlist>
+        <compatibility>Since version 2.4.69, this can be configured separately for each MDomain.</compatibility>
         <usage>
             <p>
                 This is mainly used in test setups where the module needs to

Modified: httpd/httpd/trunk/modules/md/md.h
==============================================================================
--- httpd/httpd/trunk/modules/md/md.h	Tue Jun 16 19:17:22 2026	(r1935439)
+++ httpd/httpd/trunk/modules/md/md.h	Tue Jun 16 19:28:00 2026	(r1935440)
@@ -103,6 +103,8 @@ struct md_t {
     const char *proxy_url;          /* Proxy URL, override global command */
     const char *ca_certs;           /* root certificates to use for connections,
                                        override global command */
+    const char *proxy_ca_certs;     /* root certificates to use for proxy connections,
+                                       override global command */
     const struct md_srv_conf_t *sc; /* server config where it was defined or NULL */
     const char *defn_name;          /* config file this MD was defined */
     unsigned defn_line_number;      /* line number of definition */
@@ -188,6 +190,7 @@ struct md_t {
 #define MD_KEY_PROFILE          "profile"
 #define MD_KEY_PROFILE_MANDATORY "profile-mandatory"
 #define MD_KEY_PROTO            "proto"
+#define MD_KEY_PROXY_CA_CERTS   "proxy-ca-certs"
 #define MD_KEY_PROXY_URL        "proxy-url"
 #define MD_KEY_READY            "ready"
 #define MD_KEY_REGISTRATION     "registration"

Modified: httpd/httpd/trunk/modules/md/md_acme.c
==============================================================================
--- httpd/httpd/trunk/modules/md/md_acme.c	Tue Jun 16 19:17:22 2026	(r1935439)
+++ httpd/httpd/trunk/modules/md/md_acme.c	Tue Jun 16 19:28:00 2026	(r1935440)
@@ -624,7 +624,8 @@ apr_status_t md_acme_POST_new_account(md
 /* ACME setup */
 
 apr_status_t md_acme_create(md_acme_t **pacme, apr_pool_t *p, const char *url,
-                            const char *proxy_url, const char *ca_file)
+                            const char *proxy_url, const char *ca_file,
+                            const char *proxy_ca_file)
 {
     md_acme_t *acme;
     const char *err = NULL;
@@ -650,6 +651,7 @@ apr_status_t md_acme_create(md_acme_t **
     acme->proxy_url = apr_pstrdup(p, proxy_url);
     acme->max_retries = 9;
     acme->ca_file = ca_file;
+    acme->proxy_ca_file = proxy_ca_file;
 
     if (APR_SUCCESS != (rv = apr_uri_parse(p, url, &uri_parsed))) {
         md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "parsing ACME uri: %s", url);
@@ -802,6 +804,7 @@ apr_status_t md_acme_setup(md_acme_t *ac
     md_http_set_connect_timeout_default(acme->http, apr_time_from_sec(30));
     md_http_set_stalling_default(acme->http, 10, apr_time_from_sec(30));
     md_http_set_ca_file(acme->http, acme->ca_file);
+    md_http_set_proxy_ca_file(acme->http, acme->proxy_ca_file);
     
     md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, 0, acme->p, "get directory from %s", acme->url);
     

Modified: httpd/httpd/trunk/modules/md/md_acme.h
==============================================================================
--- httpd/httpd/trunk/modules/md/md_acme.h	Tue Jun 16 19:17:22 2026	(r1935439)
+++ httpd/httpd/trunk/modules/md/md_acme.h	Tue Jun 16 19:28:00 2026	(r1935440)
@@ -98,6 +98,7 @@ struct md_acme_t {
     const char *user_agent;
     const char *proxy_url;
     const char *ca_file;
+    const char *proxy_ca_file;
     
     const char *acct_id;            /* local storage id account was loaded from or NULL */
     struct md_acme_acct_t *acct;    /* account at ACME server to use for requests */
@@ -152,9 +153,11 @@ apr_status_t md_acme_init(apr_pool_t *po
  * @param url     url of the server, optional if known at path
  * @param proxy_url optional url of a HTTP(S) proxy to use
  * @param ca_file optional CA trust anchor file to use
+ * @param proxy_ca_file optional CA trust anchor file to use for the HTTP proxy
  */
 apr_status_t md_acme_create(md_acme_t **pacme, apr_pool_t *p, const char *url,
-                            const char *proxy_url, const char *ca_file);
+                            const char *proxy_url, const char *ca_file,
+                            const char *proxy_ca_file);
 
 /**
  * Contact the ACME server and retrieve its directory information.

Modified: httpd/httpd/trunk/modules/md/md_acme_drive.c
==============================================================================
--- httpd/httpd/trunk/modules/md/md_acme_drive.c	Tue Jun 16 19:17:22 2026	(r1935439)
+++ httpd/httpd/trunk/modules/md/md_acme_drive.c	Tue Jun 16 19:28:00 2026	(r1935440)
@@ -772,7 +772,8 @@ static apr_status_t acme_renew(md_proto_
                               d->md->name, ca_effective);
     if (APR_SUCCESS != (rv = md_acme_create(&ad->acme, d->p, ca_effective,
                                             ad->md->proxy_url ? ad->md->proxy_url : d->proxy_url,
-                                            ad->md->ca_certs ? ad->md->ca_certs : d->ca_certs))) {
+                                            ad->md->ca_certs ? ad->md->ca_certs : d->ca_certs,
+                                            ad->md->proxy_ca_certs ? ad->md->proxy_ca_certs : d->proxy_ca_certs))) {
         md_result_printf(result, rv, "setup ACME communications");
         md_result_log(result, MD_LOG_ERR);
         goto out;
@@ -1035,7 +1036,8 @@ static apr_status_t acme_preload(md_prot
         
         if (APR_SUCCESS != (rv = md_acme_create(&acme, d->p, md->ca_effective,
                                                 d->md->proxy_url ? d->md->proxy_url : d->proxy_url,
-                                                d->md->ca_certs ? d->md->ca_certs : d->ca_certs))) {
+                                                d->md->ca_certs ? d->md->ca_certs : d->ca_certs,
+                                                d->md->proxy_ca_certs ? d->md->proxy_ca_certs : d->proxy_ca_certs))) {
             md_result_set(result, rv, "error setting up acme");
             goto leave;
         }
@@ -1145,7 +1147,8 @@ static apr_status_t acme_get_ari(md_prot
 
     if (APR_SUCCESS != (rv = md_acme_create(&ad->acme, d->p, ca_effective,
                                             d->md->proxy_url ? d->md->proxy_url : d->proxy_url,
-                                            d->md->ca_certs ? d->md->ca_certs : d->ca_certs))) {
+                                            d->md->ca_certs ? d->md->ca_certs : d->ca_certs,
+                                            d->md->proxy_ca_certs ? d->md->proxy_ca_certs : d->proxy_ca_certs))) {
         md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, d->p,
                       "create ACME communications");
         goto out;

Modified: httpd/httpd/trunk/modules/md/md_core.c
==============================================================================
--- httpd/httpd/trunk/modules/md/md_core.c	Tue Jun 16 19:17:22 2026	(r1935439)
+++ httpd/httpd/trunk/modules/md/md_core.c	Tue Jun 16 19:28:00 2026	(r1935440)
@@ -260,6 +260,7 @@ md_t *md_clone(apr_pool_t *p, const md_t
         if (src->dns01_cmd) md->dns01_cmd = apr_pstrdup(p, src->dns01_cmd);
         if (src->proxy_url) md->proxy_url = apr_pstrdup(p, src->proxy_url);
         if (src->ca_certs) md->ca_certs = apr_pstrdup(p, src->ca_certs);
+        if (src->proxy_ca_certs) md->proxy_ca_certs = apr_pstrdup(p, src->proxy_ca_certs);
         if (src->cert_files) md->cert_files = md_array_str_clone(p, src->cert_files);
         if (src->pkey_files) md->pkey_files = md_array_str_clone(p, src->pkey_files);
     }    
@@ -319,6 +320,7 @@ md_json_t *md_to_json(const md_t *md, ap
         if (md->dns01_cmd) md_json_sets(md->dns01_cmd, json, MD_KEY_CMD_DNS01, NULL);
         if (md->proxy_url) md_json_sets(md->proxy_url, json, MD_KEY_PROXY_URL, NULL);
         if (md->ca_certs) md_json_sets(md->ca_certs, json, MD_KEY_CA_CERTS, NULL);
+        if (md->proxy_ca_certs) md_json_sets(md->proxy_ca_certs, json, MD_KEY_PROXY_CA_CERTS, NULL);
         if (md->ca_eab_kid && strcmp("none", md->ca_eab_kid)) {
             md_json_sets(md->ca_eab_kid, json, MD_KEY_EAB, MD_KEY_KID, NULL);
             if (md->ca_eab_hmac) md_json_sets(md->ca_eab_hmac, json, MD_KEY_EAB, MD_KEY_HMAC, NULL);
@@ -390,6 +392,7 @@ md_t *md_from_json(md_json_t *json, apr_
         md->dns01_cmd = md_json_dups(p, json, MD_KEY_CMD_DNS01, NULL);
         md->proxy_url = md_json_dups(p, json, MD_KEY_PROXY_URL, NULL);
         md->ca_certs = md_json_dups(p, json, MD_KEY_CA_CERTS, NULL);
+        md->proxy_ca_certs = md_json_dups(p, json, MD_KEY_PROXY_CA_CERTS, NULL);
         if (md_json_has_key(json, MD_KEY_EAB, NULL)) {
             md->ca_eab_kid = md_json_dups(p, json, MD_KEY_EAB, MD_KEY_KID, NULL);
             md->ca_eab_hmac = md_json_dups(p, json, MD_KEY_EAB, MD_KEY_HMAC, NULL);

Modified: httpd/httpd/trunk/modules/md/md_curl.c
==============================================================================
--- httpd/httpd/trunk/modules/md/md_curl.c	Tue Jun 16 19:17:22 2026	(r1935439)
+++ httpd/httpd/trunk/modules/md/md_curl.c	Tue Jun 16 19:28:00 2026	(r1935440)
@@ -248,6 +248,7 @@ static apr_status_t internals_setup(md_h
     CURL *curl;
     apr_status_t rv = APR_SUCCESS;
     long ssl_options = 0;
+    long proxy_ssl_options = 0;
 
     curl = md_http_get_impl_data(req->http);
     if (!curl) {
@@ -315,6 +316,16 @@ static apr_status_t internals_setup(md_h
         ssl_options |= CURLSSLOPT_NO_REVOKE;
 #endif
     }
+    if (req->proxy_ca_file) {
+        curl_easy_setopt(curl, CURLOPT_PROXY_CAINFO, req->proxy_ca_file);
+        /* for a custom CA, allow certificates checking to ignore the
+         * Schannel error CRYPT_E_NO_REVOCATION_CHECK (could be a missing OCSP
+         * responder URL in the certs???). See issue #361 */
+#ifdef CURLSSLOPT_NO_REVOKE
+        proxy_ssl_options |= CURLSSLOPT_NO_REVOKE;
+#endif
+    }
+
     if (req->unix_socket_path) {
         curl_easy_setopt(curl, CURLOPT_UNIX_SOCKET_PATH, req->unix_socket_path);
     }
@@ -356,6 +367,9 @@ static apr_status_t internals_setup(md_h
     if (ssl_options)
         curl_easy_setopt(curl, CURLOPT_SSL_OPTIONS, ssl_options);
 
+    if (proxy_ssl_options)
+        curl_easy_setopt(curl, CURLOPT_PROXY_SSL_OPTIONS, proxy_ssl_options);
+
 leave:
     req->internals = (APR_SUCCESS == rv)? internals : NULL;
     return rv;

Modified: httpd/httpd/trunk/modules/md/md_http.c
==============================================================================
--- httpd/httpd/trunk/modules/md/md_http.c	Tue Jun 16 19:17:22 2026	(r1935439)
+++ httpd/httpd/trunk/modules/md/md_http.c	Tue Jun 16 19:28:00 2026	(r1935440)
@@ -36,6 +36,7 @@ struct md_http_t {
     const char *unix_socket_path;
     md_http_timeouts_t timeout;
     const char *ca_file;
+    const char *proxy_ca_file;
 };
 
 static md_http_impl_t *cur_impl;
@@ -107,6 +108,9 @@ apr_status_t md_http_clone(md_http_t **p
         if (source_http->ca_file) {
             (*phttp)->ca_file = apr_pstrdup(p, source_http->ca_file);
         }
+        if (source_http->proxy_ca_file) {
+            (*phttp)->proxy_ca_file = apr_pstrdup(p, source_http->proxy_ca_file);
+        }
     }
     return rv;
 }
@@ -163,6 +167,11 @@ void md_http_set_ca_file(md_http_t *http
     http->ca_file = ca_file;
 }
 
+void md_http_set_proxy_ca_file(md_http_t *http, const char *ca_file)
+{
+    http->proxy_ca_file = ca_file;
+}
+
 void md_http_set_unix_socket_path(md_http_t *http, const char *path)
 {
     http->unix_socket_path = path;
@@ -235,6 +244,7 @@ static apr_status_t req_create(md_http_r
     req->proxy_url = http->proxy_url;
     req->timeout = http->timeout;
     req->ca_file = http->ca_file;
+    req->proxy_ca_file = http->proxy_ca_file;
     req->unix_socket_path = http->unix_socket_path;
     *preq = req;
     return rv;

Modified: httpd/httpd/trunk/modules/md/md_http.h
==============================================================================
--- httpd/httpd/trunk/modules/md/md_http.h	Tue Jun 16 19:17:22 2026	(r1935439)
+++ httpd/httpd/trunk/modules/md/md_http.h	Tue Jun 16 19:28:00 2026	(r1935440)
@@ -65,6 +65,7 @@ struct md_http_request_t {
     const char *user_agent;
     const char *proxy_url;
     const char *ca_file;
+    const char *proxy_ca_file;
     const char *unix_socket_path;
     apr_table_t *headers;
     struct apr_bucket_brigade *body;
@@ -119,13 +120,20 @@ void md_http_set_stalling_default(md_htt
 void md_http_set_stalling(md_http_request_t *req, long bytes_per_sec, apr_time_t timeout);
 
 /**
- * Set a CA file (in PERM format) to use for root certificates when
+ * Set a CA file (in PEM format) to use for root certificates when
  * verifying SSL connections. If not set (or set to NULL), the systems
  * certificate store will be used.
  */
 void md_http_set_ca_file(md_http_t *http, const char *ca_file);
 
 /**
+ * Set a CA file (in PEM format) to use for root certificates when
+ * verifying SSL connections to the HTTP proxy. If not set (or set to NULL),
+ * the systems certificate store will be used.
+ */
+void md_http_set_proxy_ca_file(md_http_t *http, const char *ca_file);
+
+/**
  * Set the path of a unix domain socket for use instead of TCP
  * in a connection. Disable by providing NULL as path.
  */

Modified: httpd/httpd/trunk/modules/md/md_reg.c
==============================================================================
--- httpd/httpd/trunk/modules/md/md_reg.c	Tue Jun 16 19:17:22 2026	(r1935439)
+++ httpd/httpd/trunk/modules/md/md_reg.c	Tue Jun 16 19:28:00 2026	(r1935440)
@@ -48,6 +48,7 @@ struct md_reg_t {
     int can_https;
     const char *proxy_url;
     const char *ca_certs;
+    const char *proxy_ca_certs;
     int domains_frozen;
     md_timeslice_t *renew_window;
     md_timeslice_t *warn_window;
@@ -97,8 +98,9 @@ static apr_status_t load_props(md_reg_t
 
 apr_status_t md_reg_create(md_reg_t **preg, apr_pool_t *p, struct md_store_t *store,
                            const char *proxy_url, const char *ca_certs,
-                           apr_time_t min_delay, int retry_failover,
-                           int use_store_locks, apr_time_t lock_wait_timeout)
+                           const char *proxy_ca_certs, apr_time_t min_delay,
+                           int retry_failover, int use_store_locks,
+                           apr_time_t lock_wait_timeout)
 {
     md_reg_t *reg;
     apr_status_t rv;
@@ -113,6 +115,8 @@ apr_status_t md_reg_create(md_reg_t **pr
     reg->proxy_url = apr_pstrdup(p, proxy_url);
     reg->ca_certs = (ca_certs && apr_cstr_casecmp("none", ca_certs))?
                     apr_pstrdup(p, ca_certs) : NULL;
+    reg->proxy_ca_certs = (proxy_ca_certs && apr_cstr_casecmp("none", proxy_ca_certs))?
+                    apr_pstrdup(p, proxy_ca_certs) : NULL;
     reg->min_delay = min_delay;
     reg->retry_failover = retry_failover;
     reg->use_store_locks = use_store_locks;
@@ -1110,6 +1114,7 @@ static apr_status_t run_init(void *baton
     driver->store = md_reg_store_get(reg);
     driver->proxy_url = reg->proxy_url;
     driver->ca_certs = reg->ca_certs;
+    driver->proxy_ca_certs = reg->proxy_ca_certs;
     driver->md = md;
     driver->can_http = reg->can_http;
     driver->can_https = reg->can_https;

Modified: httpd/httpd/trunk/modules/md/md_reg.h
==============================================================================
--- httpd/httpd/trunk/modules/md/md_reg.h	Tue Jun 16 19:17:22 2026	(r1935439)
+++ httpd/httpd/trunk/modules/md/md_reg.h	Tue Jun 16 19:28:00 2026	(r1935440)
@@ -40,13 +40,15 @@ typedef struct md_reg_t md_reg_t;
  * @param store the store to base on
  * @param proxy_url optional URL of a proxy to use for requests
  * @param ca_certs optional CA trust anchor file to use
+ * @param proxy_ca_certs optional CA trust anchor file to use for the HTTP proxy
  * @param min_delay minimum delay between renewal attempts for a domain
  * @param retry_failover number of failed renewals attempt to fail over to alternate ACME ca
  */
 apr_status_t md_reg_create(md_reg_t **preg, apr_pool_t *pm, md_store_t *store,
                            const char *proxy_url, const char *ca_certs,
-                           apr_time_t min_delay, int retry_failover,
-                           int use_store_locks, apr_time_t lock_wait_timeout);
+                           const char *proxy_ca_certs, apr_time_t min_delay,
+                           int retry_failover, int use_store_locks,
+                           apr_time_t lock_wait_timeout);
 
 md_store_t *md_reg_store_get(md_reg_t *reg);
 
@@ -225,6 +227,7 @@ struct md_proto_driver_t {
     md_store_t *store;
     const char *proxy_url;
     const char *ca_certs;
+    const char *proxy_ca_certs;
     const md_t *md;
 
     int can_http;

Modified: httpd/httpd/trunk/modules/md/mod_md.c
==============================================================================
--- httpd/httpd/trunk/modules/md/mod_md.c	Tue Jun 16 19:17:22 2026	(r1935439)
+++ httpd/httpd/trunk/modules/md/mod_md.c	Tue Jun 16 19:28:00 2026	(r1935440)
@@ -857,6 +857,7 @@ static apr_status_t md_post_config_befor
     md_store_t *store;
     const char *proxy_url;
     const char *ca_certs;
+    const char *proxy_ca_certs;
 
     apr_pool_userdata_get(&data, mod_md_init_key, s->process->pool);
     if (data == NULL) {
@@ -897,8 +898,9 @@ static apr_status_t md_post_config_befor
 
     proxy_url = apr_table_get(mc->env, MD_KEY_PROXY_URL);
     ca_certs = apr_table_get(mc->env, MD_KEY_CA_CERTS);
+    proxy_ca_certs = apr_table_get(mc->env, MD_KEY_PROXY_CA_CERTS);
 
-    rv = md_reg_create(&mc->reg, p, store, proxy_url, ca_certs,
+    rv = md_reg_create(&mc->reg, p, store, proxy_url, ca_certs, proxy_ca_certs,
                        mc->min_delay, mc->retry_failover,
                        mc->use_store_locks, mc->lock_wait_timeout);
     if (APR_SUCCESS != rv) {

Modified: httpd/httpd/trunk/modules/md/mod_md_config.c
==============================================================================
--- httpd/httpd/trunk/modules/md/mod_md_config.c	Tue Jun 16 19:17:22 2026	(r1935439)
+++ httpd/httpd/trunk/modules/md/mod_md_config.c	Tue Jun 16 19:28:00 2026	(r1935440)
@@ -127,6 +127,7 @@ static md_srv_conf_t defconf = {
     NULL,                      /* dns01_cmd */
     NULL,                      /* proxy URL */
     NULL,                      /* CA cert file to use */
+    NULL,                      /* CA cert file to use for proxy */
     NULL,                      /* currently defined md */
     NULL,                      /* assigned md, post config */
     0,                         /* is_ssl, set during mod_ssl post_config */
@@ -187,6 +188,7 @@ static void srv_conf_props_clear(md_srv_
     sc->dns01_cmd = NULL;
     sc->proxy_url = NULL;
     sc->ca_certs = NULL;
+    sc->proxy_ca_certs = NULL;
 }
 
 static void srv_conf_props_copy(md_srv_conf_t *to, const md_srv_conf_t *from)
@@ -213,6 +215,7 @@ static void srv_conf_props_copy(md_srv_c
     to->dns01_cmd = from->dns01_cmd;
     to->proxy_url = from->proxy_url;
     to->ca_certs = from->ca_certs;
+    to->proxy_ca_certs = from->proxy_ca_certs;
 }
 
 static void srv_conf_props_apply(md_t *md, const md_srv_conf_t *from, apr_pool_t *p)
@@ -242,6 +245,7 @@ static void srv_conf_props_apply(md_t *m
     if (from->dns01_cmd) md->dns01_cmd = from->dns01_cmd;
     if (from->proxy_url) md->proxy_url = from->proxy_url;
     if (from->ca_certs) md->ca_certs = from->ca_certs;
+    if (from->proxy_ca_certs) md->proxy_ca_certs = from->proxy_ca_certs;
 }
 
 void *md_config_create_svr(apr_pool_t *pool, server_rec *s)
@@ -293,6 +297,7 @@ static void *md_config_merge(apr_pool_t
     nsc->dns01_cmd = (add->dns01_cmd)? add->dns01_cmd : base->dns01_cmd;
     nsc->proxy_url = (add->proxy_url)? add->proxy_url : base->proxy_url;
     nsc->ca_certs = (add->ca_certs)? add->ca_certs : base->ca_certs;
+    nsc->proxy_ca_certs = (add->proxy_ca_certs)? add->proxy_ca_certs : base->proxy_ca_certs;
     nsc->current = NULL;
     
     return nsc;
@@ -1273,6 +1278,25 @@ static const char *md_config_set_ca_cert
     return NULL;
 }
 
+static const char *md_config_set_proxy_ca_certs(cmd_parms *cmd, void *arg, const char *value)
+{
+    md_srv_conf_t *sc = md_config_get(cmd->server);
+    const char *err;
+
+    if ((err = md_conf_check_location(cmd, MD_LOC_ALL))) {
+        return err;
+    }
+
+    if (inside_md_section(cmd)) {
+        sc->proxy_ca_certs = value;
+    } else {
+        apr_table_set(sc->mc->env, MD_KEY_PROXY_CA_CERTS, value);
+    }
+
+    (void)arg;
+    return NULL;
+}
+
 static const char *md_config_set_eab(cmd_parms *cmd, void *dc,
                                      const char *keyid, const char *hmac)
 {
@@ -1411,6 +1435,8 @@ const command_rec md_cmds[] = {
                   "How long to delay activation of new certificates"),
     AP_INIT_TAKE1("MDCACertificateFile", md_config_set_ca_certs, NULL, RSRC_CONF,
                   "Set the CA file to use for connections"),
+    AP_INIT_TAKE1("MDHttpProxyCACertificateFile", md_config_set_proxy_ca_certs, NULL, RSRC_CONF,
+                  "Set the CA file to use for connections to the HTTP(S) proxy"),
     AP_INIT_TAKE12("MDExternalAccountBinding", md_config_set_eab, NULL, RSRC_CONF,
                   "Set the external account binding keyid and hmac values to use at CA"),
     AP_INIT_TAKE1("MDRetryDelay", md_config_set_min_delay, NULL, RSRC_CONF,

Modified: httpd/httpd/trunk/modules/md/mod_md_config.h
==============================================================================
--- httpd/httpd/trunk/modules/md/mod_md_config.h	Tue Jun 16 19:17:22 2026	(r1935439)
+++ httpd/httpd/trunk/modules/md/mod_md_config.h	Tue Jun 16 19:28:00 2026	(r1935440)
@@ -115,6 +115,8 @@ typedef struct md_srv_conf_t {
     const char *proxy_url;             /* Proxy URL, override global command */
     const char *ca_certs;              /* root certificates to use for connections,
                                           override global command */
+    const char *proxy_ca_certs;        /* root certificates to use for proxy connections,
+                                          override global command */
 
     md_t *current;                     /* md currently defined in <MDomainSet xxx> section */
     struct apr_array_header_t *assigned; /* post_config: MDs that apply to this server */