1

I'll preface by noting that I have a system set up using Google Cloud Run + Load Balancer + IAP to run a number of apps on https://example.com/app1, https://example.com/app2, etc, and up to now I've only deployed Streamlit apps this way. The load balancer is directing traffic to each app in Cloud Run according to subpath (/app1, ...), and I used the --server.baseUrlPath=app2 option of streamlit run with no problems.

Now I'm trying to get a 'hello, world' Django 4.1.5 app running on https://example.com/directory, and I can't seem to get the routes right.

The Dockerfile ends with

CMD exec poetry run gunicorn --bind 0.0.0.0:${PORT} --workers 1 --threads 8 --timeout 0 example_dir.wsgi:application

I added FORCE_SCRIPT_NAME = "/directory" in settings.py.
Here's example_dir/urls.py:

urlpatterns = urlpatterns = [
    path("admin/", admin.site.urls),
    path("", include("directory.urls")),
]

and here's directory/urls.py:

urlpatterns = [
    path("", views.hello, name="hello"),
]

Visiting https://example.com/directory returns

Page not found (404)
Request Method: GET
Request URL:    http://example.com/directory/directory
Using the URLconf defined in example_dir.urls, Django tried these URL patterns, in this order:

1. admin/
2. [name='hello']

That Request URL is surprising and weird. I'd expect the request url to be just http://example.com/directory.

Adding either USE_X_FORWARDED_HOST = True or SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') or both (per nixhive.com) did not affect the result.

Nathan Lloyd
  • 1,821
  • 4
  • 15
  • 19

1 Answers1

0

I don't think script name has anything to do with what you are trying to achieve. You need to have a WSGI app wrapping your Django app so that you can inject a path before.

There is an example on how to achieve this on Gunicorn https://github.com/benoitc/gunicorn/blob/master/examples/multiapp.py

Here is a modified version for your use case

from routes import Mapper
from example_dir.wsgi import application as app1

class Application(object):
    def __init__(self):
        self.map = Mapper()
        self.map.connect('app1', '/directory', app=app1)

    def __call__(self, environ, start_response):
        match = self.map.routematch(environ=environ)
        if not match:
            return self.error404(environ, start_response)
        return match[0]['app'](environ, start_response)

    def error404(self, environ, start_response):
        html = b"""\
        <html>
          <head>
            <title>404 - Not Found</title>
          </head>
          <body>
            <h1>404 - Not Found</h1>
          </body>
        </html>
        """
        headers = [
            ('Content-Type', 'text/html'),
            ('Content-Length', str(len(html)))
        ]
        start_response('404 Not Found', headers)
        return [html]

app = Application()
Benos
  • 676
  • 7
  • 17
  • Thanks @Benos. I tried this and upon visiting https://example.com/directory I'm getting `Page not found (404) Request Method: GET Request URL: http://example.com/directory Using the URLconf defined in example_dir.urls, Django tried these URL patterns, in this order: admin/ [name='hello'] The current path, directory, didn’t match any of these.` (NOTE: pip installed routes, hope that's the right thing) – Nathan Lloyd Jan 25 '23 at 17:40