Index: include/ap_regex.h
===================================================================
--- include/ap_regex.h	(revision 1553540)
+++ include/ap_regex.h	(working copy)
@@ -149,6 +149,15 @@
 AP_DECLARE(apr_size_t) ap_regerror(int errcode, const ap_regex_t *preg,
                                    char *errbuf, apr_size_t errbuf_size);
 
+/**
+ * Return an array of named regex backreferences
+ * @param preg The precompiled regex
+ * @param names The array to which the names will be added
+ * @param upper If non zero, uppercase the names
+ */
+AP_DECLARE(int) ap_regname(const ap_regex_t *preg,
+                           apr_array_header_t *names, int upper);
+
 /** Destroy a pre-compiled regex.
  * @param preg The pre-compiled regex to free.
  */
Index: include/http_core.h
===================================================================
--- include/http_core.h	(revision 1553540)
+++ include/http_core.h	(working copy)
@@ -617,6 +617,9 @@
     /** Max number of Range reversals (eg: 200-300, 100-125) allowed **/
     int max_reversals;
 
+    /** Named back references */
+    apr_array_header_t *refs;
+
 } core_dir_config;
 
 /* macro to implement off by default behaviour */
Index: server/core.c
===================================================================
--- server/core.c	(revision 1553540)
+++ server/core.c	(working copy)
@@ -209,6 +209,7 @@
     conf->d_is_fnmatch = new->d_is_fnmatch;
     conf->d_components = new->d_components;
     conf->r = new->r;
+    conf->refs = new->refs;
     conf->condition = new->condition;
 
     if (new->opts & OPT_UNSET) {
@@ -2159,6 +2160,11 @@
     conf->d = cmd->path;
     conf->d_is_fnmatch = (apr_fnmatch_test(conf->d) != 0);
 
+    if (r) {
+        conf->refs = apr_array_make(cmd->pool, 8, sizeof(char *));
+        ap_regname(r, conf->refs, 1);
+    }
+
     /* Make this explicit - the "/" root has 0 elements, that is, we
      * will always merge it, and it will always sort and merge first.
      * All others are sorted and tested by the number of slashes.
@@ -2235,6 +2241,11 @@
     conf->d_is_fnmatch = apr_fnmatch_test(conf->d) != 0;
     conf->r = r;
 
+    if (r) {
+        conf->refs = apr_array_make(cmd->pool, 8, sizeof(char *));
+        ap_regname(r, conf->refs, 1);
+    }
+
     ap_add_per_url_conf(cmd->server, new_url_conf);
 
     if (*arg != '\0') {
@@ -2317,6 +2328,11 @@
     conf->d_is_fnmatch = apr_fnmatch_test(conf->d) != 0;
     conf->r = r;
 
+    if (r) {
+        conf->refs = apr_array_make(cmd->pool, 8, sizeof(char *));
+        ap_regname(r, conf->refs, 1);
+    }
+
     ap_add_file_conf(cmd->pool, (core_dir_config *)mconfig, new_file_conf);
 
     if (*arg != '\0') {
Index: server/request.c
===================================================================
--- server/request.c	(revision 1553540)
+++ server/request.c	(working copy)
@@ -737,6 +737,7 @@
         apr_size_t buflen;
         char *buf;
         unsigned int seg, startseg;
+        apr_pool_t *rxpool = NULL;
 
         /* Invariant: from the first time filename_len is set until
          * it goes out of scope, filename_len==strlen(r->filename)
@@ -1192,6 +1193,10 @@
          */
         for (; sec_idx < num_sec; ++sec_idx) {
 
+            int nmatch = 0;
+            int i;
+            ap_regmatch_t *pmatch = NULL;
+
             core_dir_config *entry_core;
             entry_core = ap_get_core_module_config(sec_ent[sec_idx]);
 
@@ -1199,10 +1204,29 @@
                 continue;
             }
 
-            if (ap_regexec(entry_core->r, r->filename, 0, NULL, 0)) {
+            if (entry_core->refs && entry_core->refs->nelts) {
+                if (!rxpool) {
+                    apr_pool_create(&rxpool, r->pool);
+                }
+                nmatch = entry_core->refs->nelts;
+                pmatch = apr_palloc(rxpool, nmatch*sizeof(ap_regmatch_t));
+            }
+
+            if (ap_regexec(entry_core->r, r->filename, nmatch, pmatch, 0)) {
                 continue;
             }
 
+            for (i = 0; i < nmatch; i++) {
+                if (pmatch[i].rm_so >= 0 && pmatch[i].rm_eo >= 0 &&
+                    ((const char **)entry_core->refs->elts)[i]) {
+                    apr_table_setn(r->subprocess_env, 
+                                   ((const char **)entry_core->refs->elts)[i],
+                                   apr_pstrndup(r->pool,
+                                   r->filename + pmatch[i].rm_so,
+                                   pmatch[i].rm_eo - pmatch[i].rm_so));
+                }
+            }
+
             /* If we haven't already continue'd above, we have a match.
              *
              * Calculate our full-context core opts & override.
@@ -1241,6 +1265,10 @@
             last_walk->merged = now_merged;
         }
 
+        if (rxpool) {
+            apr_pool_destroy(rxpool);
+        }
+
         /* Whoops - everything matched in sequence, but either the original
          * walk found some additional matches (which we need to truncate), or
          * this walk found some additional matches.
@@ -1378,6 +1406,7 @@
         int matches = cache->walked->nelts;
         int cached_matches = matches;
         walk_walked_t *last_walk = (walk_walked_t*)cache->walked->elts;
+        apr_pool_t *rxpool = NULL;
 
         cached &= auth_internal_per_conf;
         cache->cached = entry_uri;
@@ -1399,16 +1428,48 @@
              * not slash terminated, then this uri must be slash
              * terminated (or at the end of the string) to match.
              */
-            if (entry_core->r
-                ? ap_regexec(entry_core->r, r->uri, 0, NULL, 0)
-                : (entry_core->d_is_fnmatch
+            if (entry_core->r) {
+
+                int nmatch = 0;
+                int i;
+                ap_regmatch_t *pmatch = NULL;
+
+                if (entry_core->refs && entry_core->refs->nelts) {
+                    if (!rxpool) {
+                        apr_pool_create(&rxpool, r->pool);
+                    }
+                    nmatch = entry_core->refs->nelts;
+                    pmatch = apr_palloc(rxpool, nmatch*sizeof(ap_regmatch_t));
+                }
+
+                if (ap_regexec(entry_core->r, r->uri, nmatch, pmatch, 0)) {
+                    continue;
+                }
+
+                for (i = 0; i < nmatch; i++) {
+                    if (pmatch[i].rm_so >= 0 && pmatch[i].rm_eo >= 0 && 
+                        ((const char **)entry_core->refs->elts)[i]) {
+                        apr_table_setn(r->subprocess_env,
+                                       ((const char **)entry_core->refs->elts)[i],
+                                       apr_pstrndup(r->pool,
+                                       r->uri + pmatch[i].rm_so,
+                                       pmatch[i].rm_eo - pmatch[i].rm_so));
+                    }
+                }
+
+            }
+            else {
+
+                if ((entry_core->d_is_fnmatch
                    ? apr_fnmatch(entry_core->d, cache->cached, APR_FNM_PATHNAME)
                    : (strncmp(entry_core->d, cache->cached, len)
                       || (len > 0
                           && entry_core->d[len - 1] != '/'
                           && cache->cached[len] != '/'
                           && cache->cached[len] != '\0')))) {
-                continue;
+                    continue;
+                }
+
             }
 
             /* If we merged this same section last time, reuse it
@@ -1443,6 +1504,10 @@
             last_walk->merged = now_merged;
         }
 
+        if (rxpool) {
+            apr_pool_destroy(rxpool);
+        }
+
         /* Whoops - everything matched in sequence, but either the original
          * walk found some additional matches (which we need to truncate), or
          * this walk found some additional matches.
@@ -1552,6 +1617,7 @@
         int matches = cache->walked->nelts;
         int cached_matches = matches;
         walk_walked_t *last_walk = (walk_walked_t*)cache->walked->elts;
+        apr_pool_t *rxpool = NULL;
 
         cached &= auth_internal_per_conf;
         cache->cached = test_file;
@@ -1564,13 +1630,43 @@
             core_dir_config *entry_core;
             entry_core = ap_get_core_module_config(sec_ent[sec_idx]);
 
-            if (entry_core->r
-                ? ap_regexec(entry_core->r, cache->cached , 0, NULL, 0)
-                : (entry_core->d_is_fnmatch
-                   ? apr_fnmatch(entry_core->d, cache->cached, APR_FNM_PATHNAME)
-                   : strcmp(entry_core->d, cache->cached))) {
-                continue;
+            if (entry_core->r) {
+
+                int nmatch = 0;
+                int i;
+                ap_regmatch_t *pmatch = NULL;
+
+                if (entry_core->refs && entry_core->refs->nelts) {
+                    if (!rxpool) {
+                        apr_pool_create(&rxpool, r->pool);
+                    }
+                    nmatch = entry_core->refs->nelts;
+                    pmatch = apr_palloc(rxpool, nmatch*sizeof(ap_regmatch_t));
+                }
+
+                if (ap_regexec(entry_core->r, cache->cached, nmatch, pmatch, 0)) {
+                    continue;
+                }
+
+                for (i = 0; i < nmatch; i++) {
+                    if (pmatch[i].rm_so >= 0 && pmatch[i].rm_eo >= 0 && 
+                        ((const char **)entry_core->refs->elts)[i]) {
+                        apr_table_setn(r->subprocess_env,
+                                       ((const char **)entry_core->refs->elts)[i],
+                                       apr_pstrndup(r->pool,
+                                       cache->cached + pmatch[i].rm_so,
+                                       pmatch[i].rm_eo - pmatch[i].rm_so));
+                    }
+                }
+
             }
+            else {
+                if ((entry_core->d_is_fnmatch
+                       ? apr_fnmatch(entry_core->d, cache->cached, APR_FNM_PATHNAME)
+                       : strcmp(entry_core->d, cache->cached))) {
+                    continue;
+                }
+            }
 
             /* If we merged this same section last time, reuse it
              */
@@ -1604,6 +1700,10 @@
             last_walk->merged = now_merged;
         }
 
+        if (rxpool) {
+            apr_pool_destroy(rxpool);
+        }
+
         /* Whoops - everything matched in sequence, but either the original
          * walk found some additional matches (which we need to truncate), or
          * this walk found some additional matches.
Index: server/util_pcre.c
===================================================================
--- server/util_pcre.c	(revision 1553540)
+++ server/util_pcre.c	(working copy)
@@ -45,6 +45,7 @@
 
 #include "httpd.h"
 #include "apr_strings.h"
+#include "apr_tables.h"
 #include "pcre.h"
 
 #define APR_WANT_STRFUNC
@@ -124,7 +125,7 @@
     const char *errorptr;
     int erroffset;
     int errcode = 0;
-    int options = 0;
+    int options = PCRE_DUPNAMES;
 
     if ((cflags & AP_REG_ICASE) != 0)
         options |= PCRE_CASELESS;
@@ -256,4 +257,38 @@
     }
 }
 
+AP_DECLARE(int) ap_regname(const ap_regex_t *preg,
+                           apr_array_header_t *names, int upper)
+{
+    int namecount;
+    int nameentrysize;
+    int i;
+    char *nametable;
+
+    pcre_fullinfo((const pcre *)preg->re_pcre, NULL,
+                       PCRE_INFO_NAMECOUNT, &namecount);
+    pcre_fullinfo((const pcre *)preg->re_pcre, NULL,
+                       PCRE_INFO_NAMEENTRYSIZE, &nameentrysize);
+    pcre_fullinfo((const pcre *)preg->re_pcre, NULL,
+                       PCRE_INFO_NAMETABLE, &nametable);
+
+    for (i = 0; i < namecount; i++) {
+        const char *offset = nametable + i * nameentrysize;
+        int capture = ((offset[0] << 8) + offset[1]);
+        while (names->nelts <= capture) {
+            apr_array_push(names);
+        }
+        if (upper) {
+            char *name = ((char **)names->elts)[capture] = 
+                apr_pstrdup(names->pool, offset + 2);
+            ap_str_toupper(name);
+        }
+        else {
+            ((const char **)names->elts)[capture] = offset + 2;
+        }
+    }
+
+    return namecount;
+}
+
 /* End of pcreposix.c */
