pythonflaskiiswaitress

Problem with file path resolution when running a flask app with waitress and IIS


I'm trying to host my app on a server I have. It's running with Flask on the backend. It works on my local host, and with waitress, but when I add IIS into it so that I can use it as a page of the default website, the filepath resolution stop working.

What I mean by that is, anything under my frontend folder cannot get found, all except the page.html template because the html does load, but not the js, css or images.

Let's say the URL that it ends up at is https://myhost/myapp

This is my file structure

myapp
└── src
    ├── backend
    │   ├── app_logic
    │   │   └── ...
    │   └── app.py
    └── frontend
        ├── css
            └── styles.css
        ├── images
            ├── image1.png
            └── image2.png
        ├── js
        │   └── my_apps_js.js
        └── templates
            └── page.html

This is my app.py (Only the routing parts)

import flask
import os
from app_logic import redacted
import tempfile
import zipfile
from werkzeug.middleware.proxy_fix import ProxyFix

APPLICATION_ROOT = '/MYAPP'

template_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), '../frontend/templates'))
static_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), '../frontend'))

app = flask.Flask(
    __name__,
    template_folder=template_dir,
    static_folder=static_dir,
    static_url_path=f'{APPLICATION_ROOT}/src/frontend'
)
app.config["APPLICATION_ROOT"] = APPLICATION_ROOT
app.wsgi_app = ProxyFix(
    app.wsgi_app, x_for=1, x_proto=1, x_host=1, x_prefix=1
)
@app.route('/')
def hello_world():
    return flask.render_template('page.html')

@app.route('/toMYAPP')
def to_my_app():
    return flask.render_template('page.html')

This is my page.html (Only the link and images section)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>MYAPP</title>
    <link rel="stylesheet" href="{{ url_for('static', filename='css/styles.css') }}">
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
    <script src="{{ url_for('static', filename='js/my_app_js.js') }}"></script>
</head>
<body style="background-color: #f0f8ff;">

    <!-- Top Left and Right Logos -->
    <img src="{{ url_for('static', filename='images/image1.png') }}" class="logo_left" >
    <img src="{{ url_for('static', filename='images/image2.png') }}" class="logo_right">

This is the command I'm using to host using waitress

> cd MYAPP/src/backend
> waitress-serve --host 127.0.0.1 --port=8081 app:app

And this is my IIS URL Rewrite rule (It is in a virtual directory under the default web site, IIS Version 10.0.x)

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <system.webServer>
    <rewrite>
      <rules>
        <rule name="ReverseProxyToFlask" stopProcessing="true">
          <match url="^(.*)$" />
          <action type="Rewrite" url="http://localhost:8081/{R:1}" />
                    <serverVariables>
                    </serverVariables>
        </rule>
      </rules>
    </rewrite>
  </system.webServer>
</configuration>

This is my first time with this kind of set up, so if something looks off let me know or if any additional info would be useful

I've tried playing around with the static_url_path. These are the values I can remember I've tried

Also tried removing static_url_path and just using static_folder but that doesn't work either. Before using IIS, I did not have the static_url_path and that worked.

All of these give me a 404 (not found) error on the js,css and png files


Solution

  • Found a working solution, in case this ever helps anyone.

    The rewrite rule would leave out the /MyApp, which is what caused the 404 error. To include it, and for the app to handle it properly, we add the following:

    In app.py:

    from werkzeug.middleware.dispatcher import DispatcherMiddleware
    
    application = DispatcherMiddleware(None, {
        '/MyApp': app
    })
    

    (Can remove the ProxyFix as this makes it unnecessary)

    In web.config for the rewrite rule:

    <configuration>
      <system.webServer>
        <rewrite>
          <rules>
            <rule name="ReverseProxyToFlask" stopProcessing="true">
              <match url=".*" />
              <action type="Rewrite" url="http://127.0.0.1:8081/MyApp/{R:0}" />
            </rule>
          </rules>
        </rewrite>
        <staticContent>
          <remove fileExtension=".css" />
          <remove fileExtension=".js" />
          <remove fileExtension=".png" />
        </staticContent>
      </system.webServer>
    </configuration>
    

    (This adds the MyApp to the rewrite, and removes file extension from IIS so it doesn't try to handle them itself. Just in case)

    Not entirely sure of the implications of this fix, but this got it working perfectly.

    Once that is done, host using waitress like this:

    > cd MYAPP/src/backend
    > waitress-serve --host 127.0.0.1 --port=8081 app:application