Answer, courtesy of mwop:
When you call pipe() with an initial $path argument (as I the gist below), the middleware associated only executes if that path prefix is matched. As such, you can have that in production safely.
RemoveDevPrefixMiddleware removes the extra URL path prefix, allowing you to keep the same route config in your code for both environments.
Alternately, you could do the above within a delegator factory on the Application service; it could check to see if a particular config entry is enabled (perhaps
$config['path_prefix']), and, if so, pipe in the custom middleware before returning the application; if not, it would just return the application instance unmodified. See https://docs.zendframework.com/zend-expressive/cookbook/autowiring-routes-and-pipelines/ for details. Since the delegator factory is executed before your config/pipeline.php is invoked, this would operate before routing as well.
If you go this latter route, you could define the
$config['path_prefix'] entry in a
config/autoload/*.local.php file; that way, the entry isn’t even present in production.
Even better: you could attach the delegator within that same configuration file, so the delegator isn’t even present in production.