Many, many thanks for the input everyone - naturally it turned out to be
a case of RTFM but here's a summary of the simplest ways to do it. I've
no idea how I missed these in the docs, though I am at the
bang-head-on-wall stage of development.

(I'm going on the assumption that the proxy may not be running mod_perl
to keep memory footprint down)

1 - RequestHeader directive in the HTTPS proxy - avoids listening on 2
ports but runs late in the request (fixup) - fine for proxying.

2 - $r->connection()->local_addr()->port(); (I missed that in the docs
by assuming local_addr returned an IP address string, not an
APR::Socket). It's a clean way but differing by port number is not a
flexible way to set up the application, makes it less portable.

I've just spent half an hour creating this (Apache) config snippet which
can be included in both the HTTP and HTTPS proxies:

SetEnv SCHEME http
SetEnv HOST localhost
RewriteEngine on
RewriteCond %{HTTPS} on
RewriteRule ^(.*) $1 [E=SCHEME:https]
RewriteCond %{HTTP_HOST} ^(.*)$
RewriteRule ^(.*) $1 [PT,E=HOST:%1] <--------- note the
PT flag, allows mod_proxy to work!
RequestHeader set X-Absolute-URI %{SCHEME}e://%{HOST}e

and make the app allow the absolute URI to be overridden by that header.

This leaves one small security hole - any server this app is installed
on must prevent browsers from setting that header, otherwise Bad
Things(tm) may happen.