diff --git a/mercurial/hgweb/hgweb_mod.py b/mercurial/hgweb/hgweb_mod.py
--- a/mercurial/hgweb/hgweb_mod.py
+++ b/mercurial/hgweb/hgweb_mod.py
@@ -304,7 +304,7 @@ class hgweb(object):
                     yield r
 
     def _runwsgi(self, wsgireq, repo):
-        req = requestmod.parserequestfromenv(wsgireq.env)
+        req = wsgireq.req
         rctx = requestcontext(self, repo)
 
         # This state is global across all threads.
diff --git a/mercurial/hgweb/hgwebdir_mod.py b/mercurial/hgweb/hgwebdir_mod.py
--- a/mercurial/hgweb/hgwebdir_mod.py
+++ b/mercurial/hgweb/hgwebdir_mod.py
@@ -287,6 +287,11 @@ class hgwebdir(object):
                 real = repos.get(virtualrepo)
                 if real:
                     wsgireq.env['REPO_NAME'] = virtualrepo
+                    # We have to re-parse because of updated environment
+                    # variable.
+                    # TODO this is kind of hacky and we should have a better
+                    # way of doing this than with REPO_NAME side-effects.
+                    wsgireq.req = requestmod.parserequestfromenv(wsgireq.env)
                     try:
                         # ensure caller gets private copy of ui
                         repo = hg.repository(self.ui.copy(), real)
diff --git a/mercurial/hgweb/request.py b/mercurial/hgweb/request.py
--- a/mercurial/hgweb/request.py
+++ b/mercurial/hgweb/request.py
@@ -254,6 +254,8 @@ class wsgirequest(object):
         self.server_write = None
         self.headers = []
 
+        self.req = parserequestfromenv(wsgienv)
+
     def respond(self, status, type, filename=None, body=None):
         if not isinstance(type, str):
             type = pycompat.sysstr(type)