Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get apache to serve static files on Flask webapp

I'm getting a 500 internal error while trying to get Apache to serve my static files.

The application will be locally hosted (not www facing). There will be no DNS to resolve a 'www.domain.com' name. I want to be able to access the application by entering the IP address of the server when I'm on that network.

This is my httpd.conf file (I'm on RHEL):

<Directory /var/www/testapp>
  Order allow,deny
  Allow from all
</Directory>

WSGIScriptAlias / /var/www/testapp/service.wsgi

If I change the WSGIScriptAlias to WGSIScriptAlias /test /var/www/testapp/service.wsgi then I can view my static files when I type in the IP, but I still can't access the service.py script from [IP]/test.

In any case, I want to be able to service all GET/POST requests with the service.py script so I want my alias to start at /, not some other place.

All my static files are in /var/www/html (Apache was automatically displaying these files before I messed with the httpd.conf, now I'm just getting a 500).

This is my service.wsgi:

import sys
sys.path.insert(0, '/var/www/testapp')
from service import app as application

This is my service.py:

from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello(environ, start_response):
    status = '200 OK'
    output = "Hello"
    response_headers = [('Content-type', 'text/plain'), ('Content-length', str(len(output)))]
    start_response(status, response_headers)
    return output

if __name__=='__main__'
    app.run()

Do I need keep my .wsgi files in the /var/www/html directory as well? Or can they go in a different folder? I can see that there might be some conflict between the message I am sending to the server ('Hello') and the static files that are already in the /var/www/html/ directory. That's why I tried setting the alias to /test but that didn't work either.

I just want my Flask application to service GET/POST requests and want apache to serve all the static files.

like image 626
pradyuman Avatar asked Jul 08 '15 16:07

pradyuman


People also ask

How do you serve a static file in a Flask?

Usually, the web server is configured to serve them for you, but during the development, these files are served from static folder in your package or next to your module and it will be available at /static on the application. A special endpoint 'static' is used to generate URL for static files.

Does Apache serve static files?

Static content (not regularly Changed) is one of the important elements in a Web application. As a part of serving static content we use Apache server by placing it as a Front-end Server. Apache server performs better when serving static content when compared with other servers.

Where does Flask look for static files?

Static files in Flask have a special route. All application URLs that begin with "/static", by convention, are served from a folder located at "/static" inside your application's root folder.


1 Answers

Fixing the 500 errors

You are currently getting 500 errors because your handler is a basic WSGI handler, but Flask handlers are not WSGI handlers (Flask / Werkzeug abstracts all that for you). Change your handler to:

@app.route("/")
def hello():
    return "Hello"

and the 500 errors should go away.

Serving static files with Apache

The following techniques can be used when your application is serving the root of the domain (/), depending on whether you are using WSGIScriptAlias or AddHandler.

When using WSGIScriptAlias

When using the WSGIScriptAlias to mount a WSGI application at / you can use an Apache Alias directive to ensure that certain sub-routes are not handled by WSGIScriptAlias (this is further documented in mod_wsgi's wiki as well):

Alias "/static/" "/path/to/app/static/"
<Directory "/path/to/app/static/">
  Order allow,deny
  Allow from all
</Directory>

If you also want to support blueprint static folders as well you'll also need to use the AliasMatch directive:

AliasMatch "(?i)^/([^/]+)/static/(.*)$" "/path/to/app/blueprints-root/$1/static/$2"
<Directory ~ "/path/to/app/blueprints-root/[^/]+/static/.*">
  Order allow,deny
  Allow from all
</Directory>

See also: The Directory directive.

When using AddHandler

As Graham Dumpleton has pointed out in the comments, you can use mod_rewrite to pass requests off to Python if and only if a file does not exist in DocumentRoot. Quoting from the linked docs:

When using the AddHandler directive, with WSGI applications identified by the extension of the script file, the only way to make the WSGI application appear as the root of the server is to perform on the fly rewriting of the URL internal to Apache using mod_rewrite. The required rules for mod_rewrite to ensure that a WSGI application, implemented by the script file 'site.wsgi' in the root directory of the virtual host, appears as being mounted on the root of the virtual host would be:

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ /site.wsgi/$1 [QSA,PT,L]

Do note however that when the WSGI application is executed for a request the 'SCRIPT_NAME' variable indicating what the mount point of the application was will be '/site.wsgi'. This will mean that when a WSGI application constructs an absolute URL based on 'SCRIPT_NAME', it will include 'site.wsgi' in the URL rather than it being hidden. As this would probably be undesirable, many web frameworks provide an option to override what the value for the mount point is. If such a configuration option isn't available, it is just as easy to adjust the value of 'SCRIPT_NAME' in the 'site.wsgi' script file itself.

from your.app import app  # Your Flask app

import posixpath

def application(environ, start_response):
    # Wrapper to set SCRIPT_NAME to actual mount point.
    environ['SCRIPT_NAME'] = posixpath.dirname(environ['SCRIPT_NAME'])
    if environ['SCRIPT_NAME'] == '/':
        environ['SCRIPT_NAME'] = ''
    return app(environ, start_response)

This wrapper will ensure that 'site.wsgi' never appears in the URL as long as it wasn't included in the first place and that access was always via the root of the web site instead.

like image 170
Sean Vieira Avatar answered Oct 15 '22 00:10

Sean Vieira