How to Get Apache to Serve Static Files on Flask Webapp

How to get apache to serve static files on Flask webapp

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.

How to serve static files in Flask

In production, configure the HTTP server (Nginx, Apache, etc.) in front of your application to serve requests to /static from the static folder. A dedicated web server is very good at serving static files efficiently, although you probably won't notice a difference compared to Flask at low volumes.

Flask automatically creates a /static/<path:filename> route that will serve any filename under the static folder next to the Python module that defines your Flask app. Use url_for to link to static files: url_for('static', filename='js/analytics.js')

You can also use send_from_directory to serve files from a directory in your own route. This takes a base directory and a path, and ensures that the path is contained in the directory, which makes it safe to accept user-provided paths. This can be useful in cases where you want to check something before serving the file, such as if the logged in user has permission.

from flask import send_from_directory

@app.route('/reports/<path:path>')
def send_report(path):
return send_from_directory('reports', path)

Do not use send_file or send_static_file with a user-supplied path. This will expose you to directory traversal attacks. send_from_directory was designed to safely handle user-supplied paths under a known directory, and will raise an error if the path attempts to escape the directory.

If you are generating a file in memory without writing it to the filesystem, you can pass a BytesIO object to send_file to serve it like a file. You'll need to pass other arguments to send_file in this case since it can't infer things like the file name or content type.

Static files not found Flask on Apache

You should only use an Alias for your /static/ routes if you don't want Apache to use your WSGIScriptAlias to handle matching requests. This is good for performance, since requests for static files don't need to engage the WSGI application (except creating the URL), but it may be related to your problems.

You can troubleshoot by removing:

Alias /static/ /var/www/japanesepractice.local/static
<Directory /var/www/japanesepractice.local/static>
Order allow,deny
Allow from all
</Directory>

If removing this works, try re-adding it with balanced trailing slashes (/static/ as /static to match the missing slash /var/www/japanesepractice.local/static.

mod_wsg with flask and static files served by apache

Remove:

Alias / /var/www/incubator-web/html/

add:

DocumentRoot /var/www/incubator-web/html/

Setting Alias as / hides the WSGI application as Alias takes precendence over WSGIScriptAlias. DocumentRoot is the correct way of specifying the default document directory.

Deploy Flask app as single scripts in Apache

There's nothing wrong with using Flask to power an API that is consumed by a static site - the simplest way to do what you are looking to do is to simply mount your WSGI application at some sub-path that Flask will handle (for example /api) and then have your JavaScript / form submissions / etc. just point at the appropriate endpoints under /api.

In general, you can use the same server to serve both the static content and your WSGI application, but there's no need for them to be the same. All of your static content might be served out of a CDN, with your API living on a single machine somewhere on AWS (for example).



Related Topics



Leave a reply



Submit