Deploy flask application on 1&1 shared hosting (with CGI)

10,257

Solution 1

I found a solution! I left the /$1 away from my .htaccess and modified werkzeug a bit because the environment variables in CGI are named a bit different the in WSGI applications. I'll inform the werkzeug developer about it and maybe he'll include my solution into werkzeug.

Solution 2

I'm writing in to provide an answer after nearly a year because the given answer is incomplete and because the suggestion to leave off the /$1 is wrong. Other stackoverflow threads that can be reached by an Internet search using the string "deploy flask on cgi" have also ended without satisfactory solutions.

To begin, my .htaccess file is exactly as in the referenced "flask via CGI" doc, except that the comment in the second line for the RewriteCond has to be removed because in .htaccess any comment must occupy an entire line.

I put the .htaccess file in the public_html document root folder and my cgi script is /home/myusername/public_html/scgi-bin/moc/cgiappserver-prod.cgi.

It's Python of course and the shebang at the top had better be right. At my ISP they use cpanel which has a wrapper for CGI that they call "scgi". It's not the real thing, unfortunately. So treat it as ordinary CGI for purposes of running Flask.

I should add that I only have a shared-hosting account.

Here's my cgiappserver-prod.cgi file:

#!/home/myusername/local/bin/python
import cgitb; cgitb.enable()  # This line enables CGI error reporting
from wsgiref.handlers import CGIHandler
import traceback
from settings import LGGR

app = None
try:
    import moc
    app = moc.app
except Exception, e:
    LGGR.info( traceback.format_exc([10]) )
    LGGR.info( 'Problem in cgiappserver-prod with moc import: %s' % e )

class ScriptNameStripper(object):
   def __init__(self, app):
       self.app = app
   def __call__(self, environ, start_response):
       environ['SCRIPT_NAME'] = ''
       return self.app(environ, start_response)

app = ScriptNameStripper(app)

try:
    CGIHandler().run(app)
except Exception, e:
    LGGR.info( traceback.format_exc([10]) )
    LGGR.info( 'Problem in cgiappserver-prod with CGIHandler().run(): %s' % e )

So my app is spread over a few files, with setting.py and moc.py in particular showing in the code above.

My hours of foundering around were partly due to all of the unhelpful posts on this subject that I read, but mainly due to my not getting with the business of getting error messages out early enough. (I have access to an error log that is provided by the ISP but it is seldom helpful.)

To start, I have confirmed that the cgitb.enable() function works. I have deliberately misspelled wsgiref and seen a beautiful error page and I have commented out the cgitb (cgi traceback) line to see the error message turn into a useless 500 status code.

Note that I also set up in settings.py a logger, a rotating file logger LGGR. With it I discovered that I had to do something extra--- not shown here--- to tell the Python interpreter where the sqlite3 library is.

Also, you can simply use print statements, about which the referenced Flask docs on CGI say:

  • With CGI, you will also have to make sure that your code does not contain any print statements, or that sys.stdout is overridden by something that doesn’t write into the HTTP response.

That's true, but it's helpful while debugging to see the print write into the HTTP response.

Finally, when I eventually got it working the location box of the browser sadly had stuff like www.mysite.com/scgi-bin/moc/cgiappserver-prod.cgi/contact in it, whereas I really needed simply www.mysite.com/contact.

The cure was that ScriptNameStripper class in cgiappserver-prod.cgi. I got it from other Flask docs.

Solution 3

I will try to show what I've done and it is working in Godaddy sharing host account:

In the cgi-bin folder in MYSITE folder, I added the following cgi file:

#!/home/USERNAME/.local/bin/python3
from wsgiref.handlers import CGIHandler

from sys import path
path.insert(0, '/home/USERNAME/public_html/MYSITE/')
from __init__ import app

class ProxyFix(object):
   def __init__(self, app):
       self.app = app

   def __call__(self, environ, start_response):
       environ['SERVER_NAME'] = ""
       environ['SERVER_PORT'] = "80"
       environ['REQUEST_METHOD'] = "GET"
       environ['SCRIPT_NAME'] = ""
       environ['PATH_INFO'] = "/"
       environ['QUERY_STRING'] = ""
       environ['SERVER_PROTOCOL'] = "HTTP/1.1"
       return self.app(environ, start_response)

if __name__ == '__main__':
    app.wsgi_app = ProxyFix(app.wsgi_app)
    CGIHandler().run(app)

As you can see the init file in the MYSITE folder have the flask app.

The most important thing is to set the permissions right. I setted 755 to this folder permission AS WELL AS to "/home/USERNAME/.local/bin/python3" folder!! Remember that the system needs this permission to open flask and all python packages.

For debugging I recommend visiting via ssh terminal your site and just run the CGI alone. If it works, but in the web don't, it means probably you need to give more permissions or you wrote wrong the .htaccess file.

To open the cgi I have the following .htaccess file in MYSITE folder:

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f 
RewriteRule ^(.*)$ /home/USERNAME/public_html/MYSITE/cgi-bin/application.cgi/$1 [L]

So it will render the cgi file when someone enters your page.

Share:
10,257
Sebastian Bechtel
Author by

Sebastian Bechtel

Updated on June 17, 2022

Comments

  • Sebastian Bechtel
    Sebastian Bechtel almost 2 years

    I've written a web application for my sports club with the flask web framework. I did everything on my local machine with the build-in test server.

    Know they told me to deploy it on an 1&1 shared hosting web space. They have python support but it seems like they only allow CGI to run python scripts.

    I tried this tutorial: flask via CGI

    I ignored the rewrite stuff until now. All requests to my CGI script resulted in a 404 error. I modified my 404 handler in the application to return request.path. When I request /foo/runserver.cgi/ it returns / as output. I have no idea why it doesn't serve the index view. It doesn't work with any view, I always get a 404.

    Kind regards, Sebastian

  • has2k1
    has2k1 over 10 years
    What modifications did you make to werkzeug?
  • Enrico
    Enrico over 10 years
    I'm curious which environ variables you modified too
  • Exegesis
    Exegesis over 9 years
    Can you please provide a solution? My flask cgi program works with the "$1", but I cannot route.
  • One
    One about 9 years
    This really helped running a Flask application under XAMPP. If every .py file is run with Python (one can configure that), the cgiappserver-prod.cgi might be called cgiappserver-prod.py.
  • Ciprian Tomoiagă
    Ciprian Tomoiagă almost 6 years
    while this seems to work, it overrides any info about the actual request and the app only sees the data that was setup in the environ variable. In other words, routing doesn't work
  • Ciprian Tomoiagă
    Ciprian Tomoiagă almost 6 years
    consider adding more details why leaving $1 worked for you or where you contacted the developer
  • Admin
    Admin over 5 years
    Be careful with static environment, it didn't work for me (1and1 shared hosting)