A few times over the years I have attempted to use Apache’s mod_proxy_http mod_proxy to expose services for various reasons. I, perhaps foolishly, wanted to access my “hello world” flask apps from the internet. Say you have the typical flask app running from flask on http://localhost:5000/ and want to expose that on example.com/5000. I did it with Apache.

  1. ProxyPass
  2. ProxyPassReverse
  3. SCRIPT_NAME
  4. ProxyFix

ProxyPass

You might try an Apache config like

<Location "/5000">
ProxyPass "http://localhost:5000"
</Location> 

ProxyPassReverse

But you will find that flask’s redirect function points a client to localhost rather than example.com. Mod_proxy provides the ProxyPassReverse directive to modify outgoing http headers.

<Location "/5000">
ProxyPass "http://localhost:5000"
ProxyPassReverse "http://localhost:5000"
</Location>

The redirects are now half handled. They will redirect to the correct host without the path we set in the first section.

SCRIPT_NAME

After reading this helpful gist, I began to understand the parameters of the problem. Without more information, our app cannot tell the request is coming from localhost . Some headers are set automatically, but the de-facto header for a sub-path X-Forwarded-Prefix is not. To set this header, add RequestHeader to the location block:

<Location "/5000">
ProxyPass "http://localhost:5000"
ProxyPassReverse "http://localhost:5000"
RequestHeader set X-Forwarded-Prefix "/5000"
</Location> 

ProxyFix

Then you will need to hook in the ProxyFix middleware included with Werkzeug:

from werkzeug.middleware.proxy_fix import ProxyFix
...
app.wsgi_app = ProxyFix(app.wsgi_app, x_prefix=1)

Note the documentation for ProxyFix does not use app.wsgi_app. This is because Flask utilizes Werkzeug as a component, but in the Werkzeug docs it is the first-class “app” itself.

See also: