Dash "Loading..." failure in Apache CGI

Another topic (Deploy Dash on apache server [solved!] - #26 by SebAcou) talks about the problem I am encountering, but ultimately does not give me enough information, so I’m starting a new topic.

I am forced to run CGI in Apache2 in Centos7 in a hosted VPS (virtual private server). I have root access. Not able to run WSGI.

dash==1.9.1
dash-core-components==1.8.1
dash-html-components==1.0.2
dash-renderer==1.2.4
dash-table==4.6.1

I start my dash app from CGI (see below). The dash app code does this:

app = dash.Dash(__name__)

The problem I see are failures like this:

GET http://<our_domain>/_dash-component-suites/dash_renderer/react@16.v1_2_2m1585338485.8.6.min.js net::ERR_ABORTED 404 (Not Found)

And the browser shows “Loading…” and nothing else.

From the previously referenced thread, I know that I need to start Dash with url_base_pathname, like this:

app = dash.Dash(__name__, url_base_pathname='/dash_test/')

When I run with url_base_pathname, I get error in the browser: Not Found. The requested URL was not found on the server.

My key problem is that I don’t know where to put dash_test.py, hence don’t know the correct value for url_base_pathname. I think my problem would be solved if I only knew how to set url_base_pathname!

Since this is Apache CGI, I put dash_test.cgi into html/cgi-bin:

#!/home/jkhyrsmy/venv36/weather/bin/python
# Python virtualenv must be activated
activate_this = '/home/jkhyrsmy/venv36/weather/bin/activate_this.py'

with open(activate_this) as f:
    code = compile(f.read(), activate_this, 'exec')
    exec(code, dict(__file__=activate_this))

from wsgiref.handlers import CGIHandler
from dash_test import app

CGIHandler().run(app.server)

The CGI script is correct, I can use this script to instead load my “flask_test” and it works.

Here is the dash_test app:

# -*- coding: utf-8 -*-
import dash
import dash_core_components as dcc
import dash_html_components as html

app = dash.Dash(__name__, url_base_pathname='/dash_test/')
server = app.server
app.scripts.config.serve_locally = True
app.css.config.serve_locally = True

app.layout = html.Div(children=[ html.H1('Hello Dash'), ])

if __name__ == '__main__':
    app.run_server(debug=True, port=8050)

dash_test.py is pretty simple, but I don’t know where to put it. Can I just drop it into html/cgi-bin? Should I instead make a subir, html/cgi-bin/dash_test and then put the Python Dash app in it with init.py? Should it be somewhere else and then I need to add another ScriptAlias to httpd.conf? Do I need to add ProxyPass directives in httpd.conf?

I have tried all of these things, but I just can’t get it to work. So I think I have these few questions:
Where should I put dash_test.py on my server?
What directives do I need in Apache httpd.conf?
What value do I need for Dash url_base_pathname?

I assumed that WSGI would solve my troubles (since no one else who has problems with CGI has ever gotten an answer here). So I found a way to install mod_wsgi in Apache 2.4, but this has been no help for the 404 errors:
GET http://oos.mira3.org/pyapps/_dash-component-suites/dash_html_components/dash_html_components.v1_0_2m1585268076.min.js net::ERR_ABORTED 404 (Not Found)
WSGI scripts are able to successfully run a Flask test app and also startup the Dash test app, but with all of dcc missing, the app will not go very far.
Would someone please explain where _dash-component-suites should be found? I assume that with this code:

app = dash.Dash(__name__, url_base_pathname='/pyapps/')
server = app.server
app.scripts.config.serve_locally = True
app.css.config.serve_locally = True

the Dash class should create _dash-component-suites in /pyapps/. But apparently not! So where is it?
Please look at my version list and tell me if I have some problem:

dash==1.9.1
dash-core-components==1.8.1
dash-html-components==1.0.2
dash-renderer==1.2.4
dash-table==4.6.1

I was hoping that the team would have some real world examples for deployment and an explanation of what goes on behind the scenes, but all I see is this useless page: http://dash.plotly.com/deployment.

@dr_glenn did you get to a resolution for this? I am having exactly the same issue: 404 on _dash-component-suites when running Dash as a CGI via Apache (having other issues with WSGI) and looking for pointers on where to, at least, have Apache find _dash-component-suites. Thanks!

Oh ho, this is your lucky day! I solved this about a month ago and haven’t checked back here since, mainly because I was disheartened by the lack of help here. I didn’t get a notification of your message, I just happened to visit here. Have I already said that Dash/Plotly hardly seems ready for real work? Anyway, I was feeling guilty that I have ignored others who also needed help. I didn’t want to be one of those jerks who replies, “never mind, I solved it” and then doesn’t tell you how. Rant mode off now.

For me the problem boiled down to needing help with Apache Proxy configuration. A secondary problem was getting mod_wsgi installed on a cPanel host; we have a VPS (virtual private server) from Bluehost. Our domain is mira3.org. The long guide that follows will show you how I got a dash app deployed with WSGI on Apache in a cPanel host.

Advice is to put your python web apps not in Apache cgi-bin, but rather in directories not under Apache control. In a VPS system there is a primary administrative user (not root); let’s suppose the username is admin. I put python webapps in /home/admin/pyapps.

First make sure you can run a dash development server and connect by Apache proxy. Dash dev server typically runs on localhost:8050. This is not how dash apps should be run in production, but if you can’t get this to work, you probably won’t get WSGI deployment to work. In pyapps do mkdir proxy_test.

Program dash_test_app.py is put in /home/admin/pyapps/dash_test.

# -*- coding: utf-8 -*-
# test using localhost:8050

import dash
import dash_core_components as dcc
import dash_html_components as html
app = dash.Dash(__name__)
app.config.update({
    'url_base_pathname':'/dtest/',
    'routes_pathname_prefix':'/dtest/',
    'requests_pathname_prefix':'/dtest/',
})
server = app.server
app.scripts.config.serve_locally = True
app.css.config.serve_locally = True

app.layout = html.Div(children=[
    html.H1('Hello Dash: test by proxy'),
])

if __name__ == '__main__':
    app.run_server(debug=True, port=8050)

Now you have to configure Apache. On a VPS cPanel system, you need to create a conf file that will be included when Apache boots. Look in /etc/apache2/conf/httpd.conf for specific instructions. On our system we create a file named vhost.conf (any .conf name is OK) and put it in /etc/apache2/conf.d/userdata/std/2_4/admin/mira3.org. Note that username admin and domain name mira3.org are required (but you will use whatever names you have). Here is the config:

# proxy for dash_test on localhost
ProxyPass /dtest http://127.0.0.1:8050
ProxyPassReverse /dtest http://127.0.0.1:8050

Restart Apache. The first command is required on cPanel systems in order to get your custom conf files.

/usr/local/cpanel/scripts/rebuildhttpdconf
/usr/local/cpanel/scripts/restartsrv httpd

Run you dash_test_app.py, assuming you have activated the virtualenv that has dash and plotly.
python dash_test_app.py

Web browser to http://mydomain/dtest/
If that doesn’t work, I probably can’t help you further!

Now on to the favored deployment, WSGI.
With the dev server approach above, the dash app will run with whatever Python version you choose. With mod_wsgi in Apache you are apparently stuck with the system default Python; on our Centos7 host, this happens to be Python 2.7 - yikes! Nevertheless, you will still need to create a virtualenv, but it will have to be whatever Python version your system runs; if you don’t match the system python, you’re likely to fail.
Our Apache server didn’t have mod_wsgi installed and I couldn’t install it with yum.
Evidently there are problems due to cPanel. I found that I needed to install using ea4 (Easy Apache 4) experimental resources: https://forums.cpanel.net/threads/configure-easyapache-experimental-for-python-with-mod_wsgi.647561/

yum install ea4-experimental
yum install ea-apache24-mod_wsgi

If you cannot get mod_wsgi installed, you will probably have to run as CGI.
When running with dev server (localhost) and proxy, it did not matter where the app code is located, but now it does. Create dir for this app: mkdir -p pyapps/dash_wsgi. Each app will get it’s own subdir in pyapps. This is not required, it’s just how I ended up doing it after many false steps.
I slightly modified the dash_test_app:

# -*- coding: utf-8 -*-
# test using WSGI
import sys
import dash
import dash_core_components as dcc
import dash_html_components as html

app = dash.Dash(__name__)
app.config.update({
    'url_base_pathname':'/dash_wsgi/',
    'routes_pathname_prefix':'/dash_wsgi/',
    'requests_pathname_prefix':'/dash_wsgi/',
})
server = app.server

app.scripts.config.serve_locally = True
app.css.config.serve_locally = True

app.layout = html.Div(children=[
    html.H1('Hello Dash'),
    html.H2('Magic: from HOME /pyapps/dash_wsgi'),
    html.H2(sys.version),
])

if __name__ == '__main__':
    app.run_server(debug=True, port=8050)

You have to create a WSGI file to run the dash app. In the same directory, file dash_test.wsgi:

#!/home/admin/venv27/bin/python
# dash_test.wsgi - WSGI script
# Python virtualenv must be activated
activate_this = '/home/admin/venv27/bin/activate_this.py'
with open(activate_this) as f:
    code = compile(f.read(), activate_this, 'exec')
    exec(code, dict(__file__=activate_this))
import sys
# Add to Python search path so that the app itself can be found
sys.path.insert(0,'/home/admin/pyapps/dash_wsgi')

# standard way to run python wsgi program
from dash_test_app import server as application

Finally go back to Apache config. On our host it is:
/etc/apache2/conf.d/userdata/std/2_4/jkhyrsmy/mira3.org/vhost.conf
Add WSGI config:

WSGIDaemonProcess dash_test user=admin group=admin threads=5
WSGIScriptAlias /dash_wsgi /home/admin/pyapps/dash_wsgi/dash_test.wsgi

<Directory /home/admin/pyapps/dash_wsgi>
    WSGIProcessGroup dash_test
    WSGIApplicationGroup %{GLOBAL}
    Require all granted
</Directory>

And again rebuild Apache config:

/usr/local/cpanel/scripts/rebuildhttpdconf
/usr/local/cpanel/scripts/restartsrv httpd

Web browser to http://mydomain/dash_wsgi

And there you have it!

1 Like

@dr_glenn
Thank you very much for this very explicit and very detailed post that great beginners like me need severely.

@dr_glenn Firstly, apologies for late ack. of your amazing response and thank you for the level of details that you provided.

For reasons that you mentioned - mod_wsgi is ruled out for us. I can’t compile against native Python version since that’s incompatible with a Snowflake library and mod_wsgi won’t work with my version (tried compiling from source but no luck)

I like the proxy approach a lot. We ended up staying with Flask as the web server and a proxy on the load-balancer that maintains client comms. This helps us maintain a SSL communication out through the load-balancer and still allow the base scenario of running Flask without CGI.

nice work there man nice nice