2025, Oct 20 17:32

IIS के पीछे Flask डिप्लॉय में static 404: subpath और URL Rewrite का पक्का समाधान

IIS पर URL Rewrite के साथ Flask डिप्लॉय में static 404 को ठीक करें: subpath (/MyApp) पास-थ्रू रखें, ऐप को उसी पथ पर माउंट करें और rewrite नियम अपडेट करें.

जब आप IIS के पीछे URL Rewrite के साथ Flask ऐप डिप्लॉय करते हैं, तो static एसेट अचानक गायब हो सकते हैं। HTML रेंडर हो जाता है, लेकिन css, js और images 404 लौटाते हैं। लोकल मशीन पर सब ठीक रहता है—ऐप waitress के साथ बढ़िया चलता है—पर जैसे ही उसे https://myhost/MyApp जैसी वर्चुअल डायरेक्टरी के तहत प्रॉक्सी किया जाता है, एसेट के पाथ रिजॉल्व होना बंद कर देते हैं।

समस्या को दोहराना

प्रोजेक्ट की संरचना में टेम्पलेट्स और static एसेट frontend डायरेक्टरी के भीतर हैं, जबकि Flask बैकएंड src/backend में है। ऐप को कस्टम static फ़ोल्डर और static_url_path के साथ कॉन्फ़िगर किया गया है।

import os as osmod
import flask as fk
from werkzeug.middleware.proxy_fix import ProxyFix as ProxyAdaptor
APP_ROOT = '/MYAPP'
tpl_path = osmod.path.abspath(osmod.path.join(osmod.path.dirname(__file__), '../frontend/templates'))
asset_base = osmod.path.abspath(osmod.path.join(osmod.path.dirname(__file__), '../frontend'))
svc = fk.Flask(
    __name__,
    template_folder=tpl_path,
    static_folder=asset_base,
    static_url_path=f'{APP_ROOT}/src/frontend'
)
svc.config['APPLICATION_ROOT'] = APP_ROOT
svc.wsgi_app = ProxyAdaptor(
    svc.wsgi_app, x_for=1, x_proto=1, x_host=1, x_prefix=1
)
@svc.route('/')
def index():
    return fk.render_template('page.html')
@svc.route('/toMYAPP')
def go_myapp():
    return fk.render_template('page.html')

टेम्पलेट url_for के जरिए static फाइलों को संदर्भित करता है, इसलिए Flask को उन्हें कॉन्फ़िगर किए गए static फ़ोल्डर से सर्व करना चाहिए।

<link rel="stylesheet" href="{{ url_for('static', filename='css/styles.css') }}">
<script src="{{ url_for('static', filename='js/my_app_js.js') }}"></script>
<img src="{{ url_for('static', filename='images/image1.png') }}">
<img src="{{ url_for('static', filename='images/image2.png') }}">

IIS में, एक वर्चुअल डायरेक्टरी reverse proxy नियम के जरिए अनुरोधों को waitress सर्वर पर अग्रेषित करती है।

<configuration>
  <system.webServer>
    <rewrite>
      <rules>
        <rule name="ReverseProxyToFlask" stopProcessing="true">
          <match url="^(.*)$" />
          <action type="Rewrite" url="http://localhost:8081/{R:1}" />
        </rule>
      </rules>
    </rewrite>
  </system.webServer>
</configuration>

असल में टूटता क्या है

Reverse proxy फॉरवर्डिंग करते समय /MyApp सेगमेंट हटा देता है। बैकएंड को ऐसे पाथ मिलते हैं जिनमें अब वर्चुअल डायरेक्टरी का प्रीफ़िक्स नहीं होता, इसलिए static endpoint के लिए बनाए गए सभी एसेट URL उस चीज़ से मेल नहीं खाते जिसे IIS आगे भेज रहा है। यही वजह है कि टेम्पलेट खुद तो रेंडर हो जाता है, लेकिन css, js और images 404 लौटाते हैं।

पाथ मिसमैच को साफ-सुथरे तरीके से ठीक करना

सही समाधान यह है कि reverse proxy फॉरवर्ड की गई URL में वर्चुअल डायरेक्टरी को शामिल रखे, और WSGI स्तर पर Flask ऐप को उसी सबपाथ के तहत माउंट किया जाए। इससे IIS जो भेजता है और ऐप जो अपेक्षा करता है, दोनों एक लाइन में आ जाते हैं।

DispatcherMiddleware की मदद से ऐप को /MyApp के तहत माउंट करें।

from werkzeug.middleware.dispatcher import DispatcherMiddleware as Mux
# ऊपर परिभाषित Flask इंस्टेंस `svc` को ही रखें
application = Mux(None, {
    '/MyApp': svc
})

इसके बाद ऐप स्पष्ट रूप से /MyApp के तहत सर्व होगा। ProxyFix मिडलवेयर की जरूरत नहीं रहती।

IIS की rewrite rule को ऐसा अपडेट करें कि सबपाथ सुरक्षित रहे, और सुनिश्चित करें कि Flask के लिए तय की गई static फाइल एक्सटेंशन IIS द्वारा इंटरसेप्ट न हों।

<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>

अंत में waitress चलाएं और वही WSGI एंट्री एक्सपोज़ करें जिसमें माउंट किया गया ऐप शामिल हो।

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

यह क्यों मायने रखता है

जब कोई ऐप reverse proxy के पीछे किसी सबपाथ के तहत डिप्लॉय होता है, तो अपस्ट्रीम और डाउनस्ट्रीम दोनों को प्रीफ़िक्स पर सहमति होनी चाहिए। यदि proxy प्रीफ़िक्स हटा देती है जबकि ऐप उसी प्रीफ़िक्स के साथ URL बनाता है, तो एसेट रिज़ॉल्यूशन विफल हो जाता है। प्रॉक्सी और WSGI दोनों स्तरों पर पाथ को संरेखित करने से static फाइलों के 404 रुकते हैं और रूटिंग अनुमानित रहती है।

मुख्य बातें

यदि static फाइलें लोकल पर चलती हैं लेकिन IIS के पीछे टूट जाती हैं, तो देखें क्या वर्चुअल डायरेक्टरी वाला सेगमेंट ड्रॉप हो रहा है। DispatcherMiddleware से Flask ऐप को उसी सबपाथ पर माउंट करें और URL Rewrite नियम को उस सेगमेंट को पास-थ्रू करने के लिए समायोजित करें। यदि IIS static फाइल एक्सटेंशनों को पकड़ रहा है, तो उन मैपिंग्स को हटा दें ताकि बैकएंड एसेट सर्व कर सके। इन बदलावों के साथ टेम्पलेट, css, js और images वर्चुअल डायरेक्टरी के तहत लगातार रिज़ॉल्व होंगे।

यह लेख StackOverflow पर प्रश्न (लेखक: Juan Calvo Franco) और उसी लेखक Juan Calvo Franco के उत्तर पर आधारित है।