Categories
flask python static-files

How to serve static files in Flask

743

So this is embarrassing. I’ve got an application that I threw together in Flask and for now it is just serving up a single static HTML page with some links to CSS and JS. And I can’t find where in the documentation Flask describes returning static files. Yes, I could use render_template but I know the data is not templatized. I’d have thought send_file or url_for was the right thing, but I could not get those to work. In the meantime, I am opening the files, reading content, and rigging up a Response with appropriate mimetype:

import os.path

from flask import Flask, Response


app = Flask(__name__)
app.config.from_object(__name__)


def root_dir():  # pragma: no cover
    return os.path.abspath(os.path.dirname(__file__))


def get_file(filename):  # pragma: no cover
    try:
        src = os.path.join(root_dir(), filename)
        # Figure out how flask returns static files
        # Tried:
        # - render_template
        # - send_file
        # This should not be so non-obvious
        return open(src).read()
    except IOError as exc:
        return str(exc)


@app.route("https://stackoverflow.com/", methods=['GET'])
def metrics():  # pragma: no cover
    content = get_file('jenkins_analytics.html')
    return Response(content, mimetype="text/html")


@app.route("https://stackoverflow.com/", defaults={'path': ''})
@app.route('/<path:path>')
def get_resource(path):  # pragma: no cover
    mimetypes = {
        ".css": "text/css",
        ".html": "text/html",
        ".js": "application/javascript",
    }
    complete_path = os.path.join(root_dir(), path)
    ext = os.path.splitext(path)[1]
    mimetype = mimetypes.get(ext, "text/html")
    content = get_file(complete_path)
    return Response(content, mimetype=mimetype)


if __name__ == '__main__':  # pragma: no cover
    app.run(port=80)

Someone want to give a code sample or url for this? I know this is going to be dead simple.

1

  • 13

    Please keep in mind that how you are actually “serving” the files will probably differ between production (on your web server) and development (on your local computer, or some other test area). As some answers have pointed out, you will probably NOT want to serve your static files with flask, but instead have them in their own directory and then have your actual web server (Apache, nginx, etc.) server those files directly.

    Dec 19, 2013 at 20:40

855

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.

0

    256

    If you just want to move the location of your static files, then the simplest method is to declare the paths in the constructor. In the example below, I have moved my templates and static files into a sub-folder called web.

    app = Flask(__name__,
                static_url_path="", 
                static_folder="web/static",
                template_folder="web/templates")
    
    • static_url_path="" removes any preceding path from the URL (i.e.
      the default /static).
    • static_folder="web/static" to serve any files found in the folder
      web/static as static files.
    • template_folder="web/templates" similarly, this changes the
      templates folder.

    Using this method, the following URL will return a CSS file:

    <link rel="stylesheet" type="text/css" href="https://stackoverflow.com/css/bootstrap.min.css">
    

    And finally, here’s a snap of the folder structure, where flask_server.py is the Flask instance:

    Nested Static Flask Folders

    0

      109

      You can also, and this is my favorite, set a folder as static path so that the files inside are reachable for everyone.

      app = Flask(__name__, static_url_path="/static")
      

      With that set you can use the standard HTML:

      <link rel="stylesheet" type="text/css" href="https://stackoverflow.com/static/style.css">
      

      3

      • 6

        Works well if there is file project/static/style.css available.

        Jan 17, 2018 at 16:05

      • 7

        the line “app = Flask(….)” needs “static_folder” to be a parameter too

        – Dee

        Jun 5, 2018 at 7:01

      • 11

        static_url_path='/static' is the default behaviour as of 2020, so it’s not necessary

        Jul 8, 2020 at 11:23