diff '--color=auto' -Naur a/conf/nginx.conf b/conf/nginx.conf
--- a/conf/nginx.conf	2025-08-13 14:33:41.000000000 +0000
+++ b/conf/nginx.conf	2025-10-25 03:58:33.873471429 +0000
@@ -36,8 +36,6 @@
         listen       80;
         server_name  localhost;
 
-        #charset koi8-r;
-
         #access_log  logs/host.access.log  main;
 
         location / {
diff '--color=auto' -Naur a/.github/workflows/f5_cla.yml b/.github/workflows/f5_cla.yml
--- a/.github/workflows/f5_cla.yml	1970-01-01 00:00:00.000000000 +0000
+++ b/.github/workflows/f5_cla.yml	2025-10-25 03:58:33.872629637 +0000
@@ -0,0 +1,41 @@
+---
+name: F5 CLA
+on:
+  issue_comment:
+    types: [created]
+  pull_request_target:
+    types: [opened, closed, synchronize]
+permissions: read-all
+jobs:
+  f5-cla:
+    name: F5 CLA
+    runs-on: ubuntu-24.04
+    permissions:
+      actions: write
+      pull-requests: write
+      statuses: write
+    steps:
+      - name: Run F5 Contributor License Agreement (CLA) assistant
+        if: (github.event.comment.body == 'recheck' || github.event.comment.body == 'I have hereby read the F5 CLA and agree to its terms') || github.event_name == 'pull_request_target'
+        uses: contributor-assistant/github-action@ca4a40a7d1004f18d9960b404b97e5f30a505a08 # v2.6.1
+        with:
+          # Path to the CLA document.
+          path-to-document: https://github.com/f5/f5-cla/blob/main/docs/f5_cla.md
+          # Custom CLA messages.
+          custom-notsigned-prcomment: '🎉 Thank you for your contribution! It appears you have not yet signed the [F5 Contributor License Agreement (CLA)](https://github.com/f5/f5-cla/blob/main/docs/f5_cla.md), which is required for your changes to be incorporated into an F5 Open Source Software (OSS) project. Please kindly read the [F5 CLA](https://github.com/f5/f5-cla/blob/main/docs/f5_cla.md) and reply on a new comment with the following text to agree:'
+          custom-pr-sign-comment: 'I have hereby read the F5 CLA and agree to its terms'
+          custom-allsigned-prcomment: '✅ All required contributors have signed the F5 CLA for this PR. Thank you!'
+          # Remote repository storing CLA signatures.
+          remote-organization-name: f5
+          remote-repository-name: f5-cla-data
+          # Branch where CLA signatures are stored.
+          branch: main
+          path-to-signatures: signatures/signatures.json
+          # Comma separated list of usernames for maintainers or any other individuals who should not be prompted for a CLA.
+          # NOTE: You will want to edit the usernames to suit your project needs.
+          allowlist: bot*
+          # Do not lock PRs after a merge.
+          lock-pullrequest-aftermerge: false
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+          PERSONAL_ACCESS_TOKEN: ${{ secrets.F5_CLA_TOKEN }}
diff '--color=auto' -Naur a/src/core/nginx.h b/src/core/nginx.h
--- a/src/core/nginx.h	2025-08-13 14:33:41.000000000 +0000
+++ b/src/core/nginx.h	2025-10-25 03:58:33.874529631 +0000
@@ -9,8 +9,8 @@
 #define _NGINX_H_INCLUDED_
 
 
-#define nginx_version      1029001
-#define NGINX_VERSION      "1.29.1"
+#define nginx_version      1029002
+#define NGINX_VERSION      "1.29.2"
 #define NGINX_VER          "nginx/" NGINX_VERSION
 
 #ifdef NGX_BUILD
diff '--color=auto' -Naur a/src/core/ngx_string.c b/src/core/ngx_string.c
--- a/src/core/ngx_string.c	2025-08-13 14:33:41.000000000 +0000
+++ b/src/core/ngx_string.c	2025-10-25 03:58:33.874679224 +0000
@@ -1494,8 +1494,9 @@
 uintptr_t
 ngx_escape_uri(u_char *dst, u_char *src, size_t size, ngx_uint_t type)
 {
-    ngx_uint_t      n;
+    u_char          prefix;
     uint32_t       *escape;
+    ngx_uint_t      n;
     static u_char   hex[] = "0123456789ABCDEF";
 
     /*
@@ -1633,11 +1634,36 @@
 
                     /* mail_auth is the same as memcached */
 
+                    /* " ", "+", "=", not allowed */
+
+    static uint32_t   mail_xtext[] = {
+        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
+
+                    /* ?>=< ;:98 7654 3210  /.-, +*)( '&%$ #"!  */
+        0x20000801, /* 0010 0000 0000 0000  0000 1000 0000 0001 */
+
+                    /* _^]\ [ZYX WVUT SRQP  ONML KJIH GFED CBA@ */
+        0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */
+
+                    /*  ~}| {zyx wvut srqp  onml kjih gfed cba` */
+        0x80000000, /* 1000 0000 0000 0000  0000 0000 0000 0000 */
+
+        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
+        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
+        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
+        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
+    };
+
     static uint32_t  *map[] =
-        { uri, args, uri_component, html, refresh, memcached, memcached };
+        { uri, args, uri_component, html, refresh, memcached, memcached,
+          mail_xtext };
+
+    static u_char  map_char[] =
+        { '%', '%', '%', '%', '%', '%', '%', '+' };
 
 
     escape = map[type];
+    prefix = map_char[type];
 
     if (dst == NULL) {
 
@@ -1658,7 +1684,7 @@
 
     while (size) {
         if (escape[*src >> 5] & (1U << (*src & 0x1f))) {
-            *dst++ = '%';
+            *dst++ = prefix;
             *dst++ = hex[*src >> 4];
             *dst++ = hex[*src & 0xf];
             src++;
diff '--color=auto' -Naur a/src/core/ngx_string.h b/src/core/ngx_string.h
--- a/src/core/ngx_string.h	2025-08-13 14:33:41.000000000 +0000
+++ b/src/core/ngx_string.h	2025-10-25 03:58:33.874836667 +0000
@@ -203,6 +203,7 @@
 #define NGX_ESCAPE_REFRESH        4
 #define NGX_ESCAPE_MEMCACHED      5
 #define NGX_ESCAPE_MAIL_AUTH      6
+#define NGX_ESCAPE_MAIL_XTEXT     7
 
 #define NGX_UNESCAPE_URI       1
 #define NGX_UNESCAPE_REDIRECT  2
diff '--color=auto' -Naur a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c
--- a/src/event/ngx_event_openssl.c	2025-08-13 14:33:41.000000000 +0000
+++ b/src/event/ngx_event_openssl.c	2025-10-25 03:58:33.875100225 +0000
@@ -128,6 +128,7 @@
 int  ngx_ssl_ocsp_index;
 int  ngx_ssl_index;
 int  ngx_ssl_certificate_name_index;
+int  ngx_ssl_client_hello_arg_index;
 
 
 u_char  ngx_ssl_session_buffer[NGX_SSL_MAX_SESSION_SIZE];
@@ -270,6 +271,14 @@
         return NGX_ERROR;
     }
 
+    ngx_ssl_client_hello_arg_index = SSL_CTX_get_ex_new_index(0, NULL, NULL,
+                                                              NULL, NULL);
+    if (ngx_ssl_client_hello_arg_index == -1) {
+        ngx_ssl_error(NGX_LOG_ALERT, log, 0,
+                      "SSL_CTX_get_ex_new_index() failed");
+        return NGX_ERROR;
+    }
+
     return NGX_OK;
 }
 
@@ -449,10 +458,18 @@
 {
     char            *err;
     X509            *x509, **elm;
+    u_long           n;
     EVP_PKEY        *pkey;
+    ngx_uint_t       mask;
     STACK_OF(X509)  *chain;
 
-    chain = ngx_ssl_cache_fetch(cf, NGX_SSL_CACHE_CERT, &err, cert, NULL);
+    mask = 0;
+    elm = NULL;
+
+retry:
+
+    chain = ngx_ssl_cache_fetch(cf, NGX_SSL_CACHE_CERT | mask,
+                                &err, cert, NULL);
     if (chain == NULL) {
         if (err != NULL) {
             ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
@@ -492,11 +509,16 @@
         }
     }
 
-    elm = ngx_array_push(&ssl->certs);
     if (elm == NULL) {
-        X509_free(x509);
-        sk_X509_pop_free(chain, X509_free);
-        return NGX_ERROR;
+        elm = ngx_array_push(&ssl->certs);
+        if (elm == NULL) {
+            X509_free(x509);
+            sk_X509_pop_free(chain, X509_free);
+            return NGX_ERROR;
+        }
+
+    } else {
+        X509_free(*elm);
     }
 
     *elm = x509;
@@ -519,11 +541,21 @@
     }
 
 #else
-    {
-    int  n;
 
     /* SSL_CTX_set0_chain() is only available in OpenSSL 1.0.2+ */
 
+#ifdef SSL_CTRL_CLEAR_EXTRA_CHAIN_CERTS
+    /* OpenSSL 1.0.1+ */
+    SSL_CTX_clear_extra_chain_certs(ssl->ctx);
+#else
+
+    if (ssl->ctx->extra_certs) {
+        sk_X509_pop_free(ssl->ctx->extra_certs, X509_free);
+        ssl->ctx->extra_certs = NULL;
+    }
+
+#endif
+
     n = sk_X509_num(chain);
 
     while (n--) {
@@ -539,10 +571,11 @@
     }
 
     sk_X509_free(chain);
-    }
+
 #endif
 
-    pkey = ngx_ssl_cache_fetch(cf, NGX_SSL_CACHE_PKEY, &err, key, passwords);
+    pkey = ngx_ssl_cache_fetch(cf, NGX_SSL_CACHE_PKEY | mask,
+                               &err, key, passwords);
     if (pkey == NULL) {
         if (err != NULL) {
             ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
@@ -554,9 +587,23 @@
     }
 
     if (SSL_CTX_use_PrivateKey(ssl->ctx, pkey) == 0) {
+        EVP_PKEY_free(pkey);
+
+        /* there can be mismatched pairs on uneven cache update */
+
+        n = ERR_peek_last_error();
+
+        if (ERR_GET_LIB(n) == ERR_LIB_X509
+            && ERR_GET_REASON(n) == X509_R_KEY_VALUES_MISMATCH
+            && mask == 0)
+        {
+            ERR_clear_error();
+            mask = NGX_SSL_CACHE_INVALIDATE;
+            goto retry;
+        }
+
         ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
                       "SSL_CTX_use_PrivateKey(\"%s\") failed", key->data);
-        EVP_PKEY_free(pkey);
         return NGX_ERROR;
     }
 
@@ -1645,6 +1692,118 @@
 }
 
 
+void
+ngx_ssl_set_client_hello_callback(SSL_CTX *ssl_ctx,
+    ngx_ssl_client_hello_arg *cb)
+{
+#ifdef SSL_CLIENT_HELLO_SUCCESS
+
+    SSL_CTX_set_client_hello_cb(ssl_ctx, ngx_ssl_client_hello_callback, NULL);
+    SSL_CTX_set_ex_data(ssl_ctx, ngx_ssl_client_hello_arg_index, cb);
+
+#elif defined OPENSSL_IS_BORINGSSL
+
+    SSL_CTX_set_select_certificate_cb(ssl_ctx, ngx_ssl_select_certificate);
+    SSL_CTX_set_ex_data(ssl_ctx, ngx_ssl_client_hello_arg_index, cb);
+
+#endif
+}
+
+
+#ifdef SSL_CLIENT_HELLO_SUCCESS
+
+int
+ngx_ssl_client_hello_callback(ngx_ssl_conn_t *ssl_conn, int *ad, void *arg)
+{
+    u_char                    *p;
+    size_t                     len;
+    ngx_int_t                  rc;
+    ngx_str_t                  host;
+    ngx_connection_t          *c;
+    ngx_ssl_client_hello_arg  *cb;
+
+    c = ngx_ssl_get_connection(ssl_conn);
+    cb = SSL_CTX_get_ex_data(c->ssl->session_ctx,
+                             ngx_ssl_client_hello_arg_index);
+
+    if (SSL_client_hello_get0_ext(ssl_conn, TLSEXT_TYPE_server_name,
+                                  (const unsigned char **) &p, &len)
+        == 0)
+    {
+        ngx_str_null(&host);
+        goto done;
+    }
+
+    /*
+     * RFC 6066 mandates non-zero HostName length, we follow OpenSSL.
+     * No more than one ServerName is expected.
+     */
+
+    if (len < 5
+        || (size_t) (p[0] << 8) + p[1] + 2 != len
+        || p[2] != TLSEXT_NAMETYPE_host_name
+        || (size_t) (p[3] << 8) + p[4] + 2 + 3 != len)
+    {
+        *ad = SSL_AD_DECODE_ERROR;
+        return SSL_CLIENT_HELLO_ERROR;
+    }
+
+    len -= 5;
+    p += 5;
+
+    if (len > TLSEXT_MAXLEN_host_name || ngx_strlchr(p, p + len, '\0')) {
+        *ad = SSL_AD_UNRECOGNIZED_NAME;
+        return SSL_CLIENT_HELLO_ERROR;
+    }
+
+    host.len = len;
+    host.data = p;
+
+done:
+
+    rc = cb->servername(ssl_conn, ad, &host);
+
+    if (rc == SSL_TLSEXT_ERR_ALERT_FATAL) {
+        return SSL_CLIENT_HELLO_ERROR;
+    }
+
+    return SSL_CLIENT_HELLO_SUCCESS;
+}
+
+#elif defined OPENSSL_IS_BORINGSSL
+
+enum ssl_select_cert_result_t ngx_ssl_select_certificate(
+    const SSL_CLIENT_HELLO *client_hello)
+{
+    int                        ad;
+    ngx_int_t                  rc;
+    ngx_ssl_conn_t            *ssl_conn;
+    ngx_connection_t          *c;
+    ngx_ssl_client_hello_arg  *cb;
+
+    ssl_conn = client_hello->ssl;
+    c = ngx_ssl_get_connection(ssl_conn);
+    cb = SSL_CTX_get_ex_data(c->ssl->session_ctx,
+                             ngx_ssl_client_hello_arg_index);
+
+    /*
+     * BoringSSL sends a hardcoded "handshake_failure" alert on errors,
+     * we use it to map SSL_AD_INTERNAL_ERROR.  To preserve other alert
+     * values, error handling is postponed to the servername callback.
+     */
+
+    rc = cb->servername(ssl_conn, &ad, NULL);
+
+    if (rc == SSL_TLSEXT_ERR_ALERT_FATAL && ad == SSL_AD_INTERNAL_ERROR) {
+        return ssl_select_cert_error;
+    }
+
+    return ssl_select_cert_success;
+}
+
+#endif
+
+
 ngx_int_t
 ngx_ssl_create_connection(ngx_ssl_t *ssl, ngx_connection_t *c, ngx_uint_t flags)
 {
diff '--color=auto' -Naur a/src/event/ngx_event_openssl_cache.c b/src/event/ngx_event_openssl_cache.c
--- a/src/event/ngx_event_openssl_cache.c	2025-08-13 14:33:41.000000000 +0000
+++ b/src/event/ngx_event_openssl_cache.c	2025-10-25 03:58:33.875606151 +0000
@@ -193,6 +193,7 @@
     time_t                 mtime;
     uint32_t               hash;
     ngx_int_t              rc;
+    ngx_uint_t             invalidate;
     ngx_file_uniq_t        uniq;
     ngx_file_info_t        fi;
     ngx_ssl_cache_t       *cache, *old_cache;
@@ -202,10 +203,17 @@
 
     *err = NULL;
 
+    invalidate = index & NGX_SSL_CACHE_INVALIDATE;
+    index &= ~NGX_SSL_CACHE_INVALIDATE;
+
     if (ngx_ssl_cache_init_key(cf->pool, index, path, &id) != NGX_OK) {
         return NULL;
     }
 
+    if (id.type == NGX_SSL_CACHE_DATA) {
+        invalidate = 0;
+    }
+
     cache = (ngx_ssl_cache_t *) ngx_get_conf(cf->cycle->conf_ctx,
                                              ngx_openssl_cache_module);
 
@@ -215,7 +223,12 @@
     cn = ngx_ssl_cache_lookup(cache, type, &id, hash);
 
     if (cn != NULL) {
-        return type->ref(err, cn->value);
+        if (!invalidate) {
+            return type->ref(err, cn->value);
+        }
+
+        type->free(cn->value);
+        ngx_rbtree_delete(&cache->rbtree, &cn->node);
     }
 
     value = NULL;
@@ -236,7 +249,7 @@
 
     old_cache = ngx_ssl_cache_get_old_conf(cf->cycle);
 
-    if (old_cache && old_cache->inheritable) {
+    if (old_cache && old_cache->inheritable && !invalidate) {
         cn = ngx_ssl_cache_lookup(old_cache, type, &id, hash);
 
         if (cn != NULL) {
diff '--color=auto' -Naur a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h
--- a/src/event/ngx_event_openssl.h	2025-08-13 14:33:41.000000000 +0000
+++ b/src/event/ngx_event_openssl.h	2025-10-25 03:58:33.875468730 +0000
@@ -26,14 +26,6 @@
 #include <openssl/engine.h>
 #endif
 #include <openssl/evp.h>
-#if (NGX_QUIC)
-#ifdef OPENSSL_IS_BORINGSSL
-#include <openssl/hkdf.h>
-#include <openssl/chacha.h>
-#else
-#include <openssl/kdf.h>
-#endif
-#endif
 #include <openssl/hmac.h>
 #ifndef OPENSSL_NO_OCSP
 #include <openssl/ocsp.h>
@@ -151,6 +143,7 @@
     unsigned                    in_ocsp:1;
     unsigned                    early_preread:1;
     unsigned                    write_blocked:1;
+    unsigned                    sni_accepted:1;
 };
 
 
@@ -197,6 +190,13 @@
 } ngx_ssl_session_cache_t;
 
 
+typedef int (*ngx_ssl_servername_pt)(ngx_ssl_conn_t *, int *, void *);
+
+typedef struct {
+    ngx_ssl_servername_pt       servername;
+} ngx_ssl_client_hello_arg;
+
+
 #define NGX_SSL_SSLv2    0x0002
 #define NGX_SSL_SSLv3    0x0004
 #define NGX_SSL_TLSv1    0x0008
@@ -286,6 +286,15 @@
     ngx_array_t *paths);
 ngx_int_t ngx_ssl_session_cache_init(ngx_shm_zone_t *shm_zone, void *data);
 
+void ngx_ssl_set_client_hello_callback(SSL_CTX *ssl_ctx,
+    ngx_ssl_client_hello_arg *cb);
+#ifdef SSL_CLIENT_HELLO_SUCCESS
+int ngx_ssl_client_hello_callback(ngx_ssl_conn_t *ssl_conn, int *ad, void *arg);
+#elif defined OPENSSL_IS_BORINGSSL
+enum ssl_select_cert_result_t ngx_ssl_select_certificate(
+    const SSL_CLIENT_HELLO *client_hello);
+#endif
+
 ngx_int_t ngx_ssl_create_connection(ngx_ssl_t *ssl, ngx_connection_t *c,
     ngx_uint_t flags);
 
@@ -382,6 +391,7 @@
 extern int  ngx_ssl_ocsp_index;
 extern int  ngx_ssl_index;
 extern int  ngx_ssl_certificate_name_index;
+extern int  ngx_ssl_client_hello_arg_index;
 
 
 extern u_char  ngx_ssl_session_buffer[NGX_SSL_MAX_SESSION_SIZE];
diff '--color=auto' -Naur a/src/event/quic/ngx_event_quic.h b/src/event/quic/ngx_event_quic.h
--- a/src/event/quic/ngx_event_quic.h	2025-08-13 14:33:41.000000000 +0000
+++ b/src/event/quic/ngx_event_quic.h	2025-10-25 03:58:33.875793054 +0000
@@ -18,7 +18,8 @@
 #elif (defined SSL_R_MISSING_QUIC_TRANSPORT_PARAMETERS_EXTENSION)
 #define NGX_QUIC_QUICTLS_API                 1
 
-#elif (defined OPENSSL_IS_BORINGSSL || defined LIBRESSL_VERSION_NUMBER)
+#elif (defined OPENSSL_IS_BORINGSSL || defined OPENSSL_IS_AWSLC               \
+       || defined LIBRESSL_VERSION_NUMBER)
 #define NGX_QUIC_BORINGSSL_API               1
 
 #else
diff '--color=auto' -Naur a/src/event/quic/ngx_event_quic_protection.c b/src/event/quic/ngx_event_quic_protection.c
--- a/src/event/quic/ngx_event_quic_protection.c	2025-08-13 14:33:41.000000000 +0000
+++ b/src/event/quic/ngx_event_quic_protection.c	2025-10-25 03:58:33.875985261 +0000
@@ -8,6 +8,12 @@
 #include <ngx_core.h>
 #include <ngx_event.h>
 #include <ngx_event_quic_connection.h>
+#if (NGX_QUIC_BORINGSSL_EVP_API)
+#include <openssl/hkdf.h>
+#include <openssl/chacha.h>
+#else
+#include <openssl/kdf.h>
+#endif
 
 
 /* RFC 9001, 5.4.1.  Header Protection Application: 5-byte mask */
@@ -33,7 +39,7 @@
 
 static ngx_int_t ngx_quic_crypto_open(ngx_quic_secret_t *s, ngx_str_t *out,
     const u_char *nonce, ngx_str_t *in, ngx_str_t *ad, ngx_log_t *log);
-#ifndef OPENSSL_IS_BORINGSSL
+#if !(NGX_QUIC_BORINGSSL_EVP_API)
 static ngx_int_t ngx_quic_crypto_common(ngx_quic_secret_t *s, ngx_str_t *out,
     const u_char *nonce, ngx_str_t *in, ngx_str_t *ad, ngx_log_t *log);
 #endif
@@ -58,7 +64,7 @@
     switch (id) {
 
     case TLS1_3_CK_AES_128_GCM_SHA256:
-#ifdef OPENSSL_IS_BORINGSSL
+#if (NGX_QUIC_BORINGSSL_EVP_API)
         ciphers->c = EVP_aead_aes_128_gcm();
 #else
         ciphers->c = EVP_aes_128_gcm();
@@ -69,7 +75,7 @@
         break;
 
     case TLS1_3_CK_AES_256_GCM_SHA384:
-#ifdef OPENSSL_IS_BORINGSSL
+#if (NGX_QUIC_BORINGSSL_EVP_API)
         ciphers->c = EVP_aead_aes_256_gcm();
 #else
         ciphers->c = EVP_aes_256_gcm();
@@ -80,12 +86,12 @@
         break;
 
     case TLS1_3_CK_CHACHA20_POLY1305_SHA256:
-#ifdef OPENSSL_IS_BORINGSSL
+#if (NGX_QUIC_BORINGSSL_EVP_API)
         ciphers->c = EVP_aead_chacha20_poly1305();
 #else
         ciphers->c = EVP_chacha20_poly1305();
 #endif
-#ifdef OPENSSL_IS_BORINGSSL
+#if (NGX_QUIC_BORINGSSL_EVP_API)
         ciphers->hp = (const EVP_CIPHER *) EVP_aead_chacha20_poly1305();
 #else
         ciphers->hp = EVP_chacha20();
@@ -94,7 +100,7 @@
         len = 32;
         break;
 
-#ifndef OPENSSL_IS_BORINGSSL
+#if !(NGX_QUIC_BORINGSSL_EVP_API)
     case TLS1_3_CK_AES_128_CCM_SHA256:
         ciphers->c = EVP_aes_128_ccm();
         ciphers->hp = EVP_aes_128_ctr();
@@ -263,7 +269,7 @@
 ngx_hkdf_expand(u_char *out_key, size_t out_len, const EVP_MD *digest,
     const uint8_t *prk, size_t prk_len, const u_char *info, size_t info_len)
 {
-#ifdef OPENSSL_IS_BORINGSSL
+#if (NGX_QUIC_BORINGSSL_EVP_API)
 
     if (HKDF_expand(out_key, out_len, digest, prk, prk_len, info, info_len)
         == 0)
@@ -325,7 +331,7 @@
     const u_char *secret, size_t secret_len, const u_char *salt,
     size_t salt_len)
 {
-#ifdef OPENSSL_IS_BORINGSSL
+#if (NGX_QUIC_BORINGSSL_EVP_API)
 
     if (HKDF_extract(out_key, out_len, digest, secret, secret_len, salt,
                      salt_len)
@@ -388,7 +394,7 @@
     ngx_quic_md_t *key, ngx_int_t enc, ngx_log_t *log)
 {
 
-#ifdef OPENSSL_IS_BORINGSSL
+#if (NGX_QUIC_BORINGSSL_EVP_API)
     EVP_AEAD_CTX  *ctx;
 
     ctx = EVP_AEAD_CTX_new(cipher, key->data, key->len,
@@ -448,7 +454,7 @@
 ngx_quic_crypto_open(ngx_quic_secret_t *s, ngx_str_t *out, const u_char *nonce,
     ngx_str_t *in, ngx_str_t *ad, ngx_log_t *log)
 {
-#ifdef OPENSSL_IS_BORINGSSL
+#if (NGX_QUIC_BORINGSSL_EVP_API)
     if (EVP_AEAD_CTX_open(s->ctx, out->data, &out->len, out->len, nonce,
                           s->iv.len, in->data, in->len, ad->data, ad->len)
         != 1)
@@ -468,7 +474,7 @@
 ngx_quic_crypto_seal(ngx_quic_secret_t *s, ngx_str_t *out, const u_char *nonce,
     ngx_str_t *in, ngx_str_t *ad, ngx_log_t *log)
 {
-#ifdef OPENSSL_IS_BORINGSSL
+#if (NGX_QUIC_BORINGSSL_EVP_API)
     if (EVP_AEAD_CTX_seal(s->ctx, out->data, &out->len, out->len, nonce,
                           s->iv.len, in->data, in->len, ad->data, ad->len)
         != 1)
@@ -484,7 +490,7 @@
 }
 
 
-#ifndef OPENSSL_IS_BORINGSSL
+#if !(NGX_QUIC_BORINGSSL_EVP_API)
 
 static ngx_int_t
 ngx_quic_crypto_common(ngx_quic_secret_t *s, ngx_str_t *out,
@@ -563,7 +569,7 @@
 ngx_quic_crypto_cleanup(ngx_quic_secret_t *s)
 {
     if (s->ctx) {
-#ifdef OPENSSL_IS_BORINGSSL
+#if (NGX_QUIC_BORINGSSL_EVP_API)
         EVP_AEAD_CTX_free(s->ctx);
 #else
         EVP_CIPHER_CTX_free(s->ctx);
@@ -579,7 +585,7 @@
 {
     EVP_CIPHER_CTX  *ctx;
 
-#ifdef OPENSSL_IS_BORINGSSL
+#if (NGX_QUIC_BORINGSSL_EVP_API)
     if (cipher == (EVP_CIPHER *) EVP_aead_chacha20_poly1305()) {
         /* no EVP interface */
         s->hp_ctx = NULL;
@@ -615,7 +621,7 @@
 
     ctx = s->hp_ctx;
 
-#ifdef OPENSSL_IS_BORINGSSL
+#if (NGX_QUIC_BORINGSSL_EVP_API)
     uint32_t         cnt;
 
     if (ctx == NULL) {
diff '--color=auto' -Naur a/src/event/quic/ngx_event_quic_protection.h b/src/event/quic/ngx_event_quic_protection.h
--- a/src/event/quic/ngx_event_quic_protection.h	2025-08-13 14:33:41.000000000 +0000
+++ b/src/event/quic/ngx_event_quic_protection.h	2025-10-25 03:58:33.876123126 +0000
@@ -22,10 +22,12 @@
 #define NGX_QUIC_MAX_MD_SIZE          48
 
 
-#ifdef OPENSSL_IS_BORINGSSL
+#if (defined OPENSSL_IS_BORINGSSL || defined OPENSSL_IS_AWSLC)
+#define NGX_QUIC_BORINGSSL_EVP_API    1
 #define ngx_quic_cipher_t             EVP_AEAD
 #define ngx_quic_crypto_ctx_t         EVP_AEAD_CTX
 #else
+#define NGX_QUIC_BORINGSSL_EVP_API    0
 #define ngx_quic_cipher_t             EVP_CIPHER
 #define ngx_quic_crypto_ctx_t         EVP_CIPHER_CTX
 #endif
diff '--color=auto' -Naur a/src/event/quic/ngx_event_quic_ssl.c b/src/event/quic/ngx_event_quic_ssl.c
--- a/src/event/quic/ngx_event_quic_ssl.c	2025-08-13 14:33:41.000000000 +0000
+++ b/src/event/quic/ngx_event_quic_ssl.c	2025-10-25 03:58:33.876264917 +0000
@@ -695,30 +695,35 @@
 
     ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_do_handshake: %d", n);
 
-    if (qc->error) {
-        return NGX_ERROR;
-    }
-
     if (n <= 0) {
         sslerr = SSL_get_error(ssl_conn, n);
 
         ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_get_error: %d",
                        sslerr);
 
-        if (sslerr != SSL_ERROR_WANT_READ) {
-
-            if (c->ssl->handshake_rejected) {
-                ngx_connection_error(c, 0, "handshake rejected");
-                ERR_clear_error();
+        if (c->ssl->handshake_rejected) {
+            ngx_connection_error(c, 0, "handshake rejected");
+            ERR_clear_error();
+            return NGX_ERROR;
+        }
 
-                return NGX_ERROR;
-            }
+        if (qc->error) {
+            ngx_connection_error(c, 0, "SSL_do_handshake() failed");
+            ERR_clear_error();
+            return NGX_ERROR;
+        }
 
+        if (sslerr != SSL_ERROR_WANT_READ) {
             ngx_ssl_connection_error(c, sslerr, 0, "SSL_do_handshake() failed");
             return NGX_ERROR;
         }
     }
 
+    if (qc->error) {
+        ngx_connection_error(c, 0, "SSL_do_handshake() failed");
+        return NGX_ERROR;
+    }
+
     if (!SSL_is_init_finished(ssl_conn)) {
         if (ngx_quic_keys_available(qc->keys, NGX_QUIC_ENCRYPTION_EARLY_DATA, 0)
             && qc->client_tp_done)
@@ -968,7 +973,7 @@
     }
 #endif
 
-#ifdef OPENSSL_IS_BORINGSSL
+#if (defined OPENSSL_IS_BORINGSSL || defined OPENSSL_IS_AWSLC)
     if (SSL_set_quic_early_data_context(ssl_conn, p, clen) == 0) {
         ngx_ssl_error(NGX_LOG_ALERT, c->log, 0,
                       "quic SSL_set_quic_early_data_context() failed");
diff '--color=auto' -Naur a/src/http/modules/ngx_http_index_module.c b/src/http/modules/ngx_http_index_module.c
--- a/src/http/modules/ngx_http_index_module.c	2025-08-13 14:33:41.000000000 +0000
+++ b/src/http/modules/ngx_http_index_module.c	2025-10-25 03:58:33.876400605 +0000
@@ -490,7 +490,7 @@
         if (value[i].len == 0) {
             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                                "index \"%V\" in \"index\" directive is invalid",
-                               &value[1]);
+                               &value[i]);
             return NGX_CONF_ERROR;
         }
 
diff '--color=auto' -Naur a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c
--- a/src/http/modules/ngx_http_ssl_module.c	2025-08-13 14:33:41.000000000 +0000
+++ b/src/http/modules/ngx_http_ssl_module.c	2025-10-25 03:58:33.876546842 +0000
@@ -749,6 +749,10 @@
     cln->data = &conf->ssl;
 
 #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
+    {
+    static ngx_ssl_client_hello_arg cb = { ngx_http_ssl_servername };
+
+    ngx_ssl_set_client_hello_callback(conf->ssl.ctx, &cb);
 
     if (SSL_CTX_set_tlsext_servername_callback(conf->ssl.ctx,
                                                ngx_http_ssl_servername)
@@ -759,7 +763,7 @@
             "dynamically to an OpenSSL library which has no tlsext support, "
             "therefore SNI is not available");
     }
-
+    }
 #endif
 
 #ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
diff '--color=auto' -Naur a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c
--- a/src/http/ngx_http_request.c	2025-08-13 14:33:41.000000000 +0000
+++ b/src/http/ngx_http_request.c	2025-10-25 03:58:33.876853503 +0000
@@ -891,27 +891,46 @@
         return SSL_TLSEXT_ERR_ALERT_FATAL;
     }
 
+    if (c->ssl->sni_accepted) {
+        return SSL_TLSEXT_ERR_OK;
+    }
+
+    if (c->ssl->handshake_rejected) {
+        *ad = SSL_AD_UNRECOGNIZED_NAME;
+        return SSL_TLSEXT_ERR_ALERT_FATAL;
+    }
+
     hc = c->data;
 
-    servername = SSL_get_servername(ssl_conn, TLSEXT_NAMETYPE_host_name);
+    if (arg != NULL) {
+        host = *(ngx_str_t *) arg;
 
-    if (servername == NULL) {
-        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
-                       "SSL server name: null");
-        goto done;
+        if (host.data == NULL) {
+            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
+                           "SSL server name: null");
+            goto done;
+        }
+
+    } else {
+        servername = SSL_get_servername(ssl_conn, TLSEXT_NAMETYPE_host_name);
+
+        if (servername == NULL) {
+            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
+                           "SSL server name: null");
+            goto done;
+        }
+
+        host.len = ngx_strlen(servername);
+        host.data = (u_char *) servername;
     }
 
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
-                   "SSL server name: \"%s\"", servername);
-
-    host.len = ngx_strlen(servername);
+                   "SSL server name: \"%V\"", &host);
 
     if (host.len == 0) {
         goto done;
     }
 
-    host.data = (u_char *) servername;
-
     rc = ngx_http_validate_host(&host, c->pool, 1);
 
     if (rc == NGX_ERROR) {
@@ -933,31 +952,6 @@
         goto done;
     }
 
-    sscf = ngx_http_get_module_srv_conf(cscf->ctx, ngx_http_ssl_module);
-
-#if (defined TLS1_3_VERSION                                                   \
-     && !defined LIBRESSL_VERSION_NUMBER && !defined OPENSSL_IS_BORINGSSL)
-
-    /*
-     * SSL_SESSION_get0_hostname() is only available in OpenSSL 1.1.1+,
-     * but servername being negotiated in every TLSv1.3 handshake
-     * is only returned in OpenSSL 1.1.1+ as well
-     */
-
-    if (sscf->verify) {
-        const char  *hostname;
-
-        hostname = SSL_SESSION_get0_hostname(SSL_get0_session(ssl_conn));
-
-        if (hostname != NULL && ngx_strcmp(hostname, servername) != 0) {
-            c->ssl->handshake_rejected = 1;
-            *ad = SSL_AD_ACCESS_DENIED;
-            return SSL_TLSEXT_ERR_ALERT_FATAL;
-        }
-    }
-
-#endif
-
     hc->ssl_servername = ngx_palloc(c->pool, sizeof(ngx_str_t));
     if (hc->ssl_servername == NULL) {
         goto error;
@@ -971,6 +965,8 @@
 
     ngx_set_connection_log(c, clcf->error_log);
 
+    sscf = ngx_http_get_module_srv_conf(cscf->ctx, ngx_http_ssl_module);
+
     c->ssl->buffer_size = sscf->buffer_size;
 
     if (sscf->ssl.ctx) {
@@ -1019,6 +1015,7 @@
         return SSL_TLSEXT_ERR_ALERT_FATAL;
     }
 
+    c->ssl->sni_accepted = 1;
     return SSL_TLSEXT_ERR_OK;
 
 error:
diff '--color=auto' -Naur a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c
--- a/src/http/ngx_http_upstream.c	2025-08-13 14:33:41.000000000 +0000
+++ b/src/http/ngx_http_upstream.c	2025-10-25 03:58:33.877275565 +0000
@@ -116,6 +116,10 @@
 static ngx_int_t
     ngx_http_upstream_process_cache_control(ngx_http_request_t *r,
     ngx_table_elt_t *h, ngx_uint_t offset);
+#if (NGX_HTTP_CACHE)
+static ngx_int_t ngx_http_upstream_process_delta_seconds(u_char *p,
+    u_char *last);
+#endif
 static ngx_int_t ngx_http_upstream_ignore_header_line(ngx_http_request_t *r,
     ngx_table_elt_t *h, ngx_uint_t offset);
 static ngx_int_t ngx_http_upstream_process_expires(ngx_http_request_t *r,
@@ -5066,18 +5070,9 @@
     }
 
     if (p) {
-        n = 0;
-
-        for (p += offset; p < last; p++) {
-            if (*p == ',' || *p == ';' || *p == ' ') {
-                break;
-            }
-
-            if (*p >= '0' && *p <= '9') {
-                n = n * 10 + (*p - '0');
-                continue;
-            }
+        n = ngx_http_upstream_process_delta_seconds(p + offset, last);
 
+        if (n == NGX_ERROR) {
             u->cacheable = 0;
             return NGX_OK;
         }
@@ -5087,7 +5082,8 @@
             return NGX_OK;
         }
 
-        r->cache->valid_sec = ngx_time() + n;
+        r->cache->valid_sec = ngx_min((ngx_uint_t) ngx_time() + n,
+                                      NGX_MAX_INT_T_VALUE);
         u->headers_in.expired = 0;
     }
 
@@ -5097,18 +5093,9 @@
                          23 - 1);
 
     if (p) {
-        n = 0;
-
-        for (p += 23; p < last; p++) {
-            if (*p == ',' || *p == ';' || *p == ' ') {
-                break;
-            }
-
-            if (*p >= '0' && *p <= '9') {
-                n = n * 10 + (*p - '0');
-                continue;
-            }
+        n = ngx_http_upstream_process_delta_seconds(p + 23, last);
 
+        if (n == NGX_ERROR) {
             u->cacheable = 0;
             return NGX_OK;
         }
@@ -5120,18 +5107,9 @@
     p = ngx_strlcasestrn(start, last, (u_char *) "stale-if-error=", 15 - 1);
 
     if (p) {
-        n = 0;
-
-        for (p += 15; p < last; p++) {
-            if (*p == ',' || *p == ';' || *p == ' ') {
-                break;
-            }
-
-            if (*p >= '0' && *p <= '9') {
-                n = n * 10 + (*p - '0');
-                continue;
-            }
+        n = ngx_http_upstream_process_delta_seconds(p + 15, last);
 
+        if (n == NGX_ERROR) {
             u->cacheable = 0;
             return NGX_OK;
         }
@@ -5145,6 +5123,41 @@
 }
 
 
+#if (NGX_HTTP_CACHE)
+
+static ngx_int_t
+ngx_http_upstream_process_delta_seconds(u_char *p, u_char *last)
+{
+    ngx_int_t  n, cutoff, cutlim;
+
+    cutoff = NGX_MAX_INT_T_VALUE / 10;
+    cutlim = NGX_MAX_INT_T_VALUE % 10;
+
+    n = 0;
+
+    for ( /* void */ ; p < last; p++) {
+        if (*p == ',' || *p == ';' || *p == ' ') {
+            break;
+        }
+
+        if (*p < '0' || *p > '9') {
+            return NGX_ERROR;
+        }
+
+        if (n >= cutoff && (n > cutoff || *p - '0' > cutlim)) {
+            n = NGX_MAX_INT_T_VALUE;
+            break;
+        }
+
+        n = n * 10 + (*p - '0');
+    }
+
+    return n;
+}
+
+#endif
+
+
 static ngx_int_t
 ngx_http_upstream_process_expires(ngx_http_request_t *r, ngx_table_elt_t *h,
     ngx_uint_t offset)
diff '--color=auto' -Naur a/src/mail/ngx_mail_proxy_module.c b/src/mail/ngx_mail_proxy_module.c
--- a/src/mail/ngx_mail_proxy_module.c	2025-08-13 14:33:41.000000000 +0000
+++ b/src/mail/ngx_mail_proxy_module.c	2025-10-25 03:58:33.877677334 +0000
@@ -531,6 +531,7 @@
     ngx_int_t                  rc;
     ngx_str_t                  line, auth, encoded;
     ngx_buf_t                 *b;
+    uintptr_t                  n;
     ngx_connection_t          *c;
     ngx_mail_session_t        *s;
     ngx_mail_proxy_conf_t     *pcf;
@@ -627,6 +628,10 @@
                           CRLF) - 1
                    + s->connection->addr_text.len + s->login.len + s->host.len;
 
+        n = ngx_escape_uri(NULL, s->login.data, s->login.len,
+                           NGX_ESCAPE_MAIL_XTEXT);
+        line.len += n * 2;
+
 #if (NGX_HAVE_INET6)
         if (s->connection->sockaddr->sa_family == AF_INET6) {
             line.len += sizeof("IPV6:") - 1;
@@ -654,7 +659,14 @@
 
         if (s->login.len && !pcf->smtp_auth) {
             p = ngx_cpymem(p, " LOGIN=", sizeof(" LOGIN=") - 1);
-            p = ngx_copy(p, s->login.data, s->login.len);
+
+            if (n == 0) {
+                p = ngx_copy(p, s->login.data, s->login.len);
+
+            } else {
+                p = (u_char *) ngx_escape_uri(p, s->login.data, s->login.len,
+                                              NGX_ESCAPE_MAIL_XTEXT);
+            }
         }
 
         p = ngx_cpymem(p, " NAME=", sizeof(" NAME=") - 1);
diff '--color=auto' -Naur a/src/stream/ngx_stream_ssl_module.c b/src/stream/ngx_stream_ssl_module.c
--- a/src/stream/ngx_stream_ssl_module.c	2025-08-13 14:33:41.000000000 +0000
+++ b/src/stream/ngx_stream_ssl_module.c	2025-10-25 03:58:33.877924852 +0000
@@ -555,27 +555,46 @@
         return SSL_TLSEXT_ERR_ALERT_FATAL;
     }
 
+    if (c->ssl->sni_accepted) {
+        return SSL_TLSEXT_ERR_OK;
+    }
+
+    if (c->ssl->handshake_rejected) {
+        *ad = SSL_AD_UNRECOGNIZED_NAME;
+        return SSL_TLSEXT_ERR_ALERT_FATAL;
+    }
+
     s = c->data;
 
-    servername = SSL_get_servername(ssl_conn, TLSEXT_NAMETYPE_host_name);
+    if (arg) {
+        host = *(ngx_str_t *) arg;
 
-    if (servername == NULL) {
-        ngx_log_debug0(NGX_LOG_DEBUG_STREAM, c->log, 0,
-                       "SSL server name: null");
-        goto done;
+        if (host.data == NULL) {
+            ngx_log_debug0(NGX_LOG_DEBUG_STREAM, c->log, 0,
+                           "SSL server name: null");
+            goto done;
+        }
+
+    } else {
+        servername = SSL_get_servername(ssl_conn, TLSEXT_NAMETYPE_host_name);
+
+        if (servername == NULL) {
+            ngx_log_debug0(NGX_LOG_DEBUG_STREAM, c->log, 0,
+                           "SSL server name: null");
+            goto done;
+        }
+
+        host.len = ngx_strlen(servername);
+        host.data = (u_char *) servername;
     }
 
     ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0,
-                   "SSL server name: \"%s\"", servername);
-
-    host.len = ngx_strlen(servername);
+                   "SSL server name: \"%V\"", &host);
 
     if (host.len == 0) {
         goto done;
     }
 
-    host.data = (u_char *) servername;
-
     rc = ngx_stream_validate_host(&host, c->pool, 1);
 
     if (rc == NGX_ERROR) {
@@ -596,35 +615,12 @@
         goto done;
     }
 
-    sscf = ngx_stream_get_module_srv_conf(cscf->ctx, ngx_stream_ssl_module);
-
-#if (defined TLS1_3_VERSION                                                   \
-     && !defined LIBRESSL_VERSION_NUMBER && !defined OPENSSL_IS_BORINGSSL)
-
-    /*
-     * SSL_SESSION_get0_hostname() is only available in OpenSSL 1.1.1+,
-     * but servername being negotiated in every TLSv1.3 handshake
-     * is only returned in OpenSSL 1.1.1+ as well
-     */
-
-    if (sscf->verify) {
-        const char  *hostname;
-
-        hostname = SSL_SESSION_get0_hostname(SSL_get0_session(ssl_conn));
-
-        if (hostname != NULL && ngx_strcmp(hostname, servername) != 0) {
-            c->ssl->handshake_rejected = 1;
-            *ad = SSL_AD_ACCESS_DENIED;
-            return SSL_TLSEXT_ERR_ALERT_FATAL;
-        }
-    }
-
-#endif
-
     s->srv_conf = cscf->ctx->srv_conf;
 
     ngx_set_connection_log(c, cscf->error_log);
 
+    sscf = ngx_stream_get_module_srv_conf(cscf->ctx, ngx_stream_ssl_module);
+
     if (sscf->ssl.ctx) {
         if (SSL_set_SSL_CTX(ssl_conn, sscf->ssl.ctx) == NULL) {
             goto error;
@@ -663,6 +659,7 @@
         return SSL_TLSEXT_ERR_ALERT_FATAL;
     }
 
+    c->ssl->sni_accepted = 1;
     return SSL_TLSEXT_ERR_OK;
 
 error:
@@ -1002,8 +999,14 @@
     cln->data = &conf->ssl;
 
 #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
+    {
+    static ngx_ssl_client_hello_arg cb = { ngx_stream_ssl_servername };
+
+    ngx_ssl_set_client_hello_callback(conf->ssl.ctx, &cb);
+
     SSL_CTX_set_tlsext_servername_callback(conf->ssl.ctx,
                                            ngx_stream_ssl_servername);
+    }
 #endif
 
 #ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
