37

I've read on quiet a few places that serving static files should be left to the server, for example in a couple of the answers on this SO question. But I use the OpenShift PaaS, and can't figure out how to modify the .htaccess file there.

I came across this piece of code that serves the sitemap from a template. I did that on my app for both the sitemap, and robots.txt, like so -

@app.route("/sitemap.xml")
def sitemap_xml():
    response= make_response(render_template("sitemap.xml"))
    response.headers['Content-Type'] = 'application/xml'
    return response

@app.route("/robots.txt")
def robots_txt():
    return render_template("robots.txt")

Is there any harm in this, or is my approach okay?

Community
  • 1
  • 1
elssar
  • 5,651
  • 7
  • 46
  • 71

3 Answers3

103

Put robots.txt and sitemap.xml into your app's static directory and define this view:

from flask import Flask, request, send_from_directory

@app.route('/robots.txt')
@app.route('/sitemap.xml')
def static_from_root():
    return send_from_directory(app.static_folder, request.path[1:])
Audrius Kažukauskas
  • 13,173
  • 4
  • 53
  • 54
  • 1
    thanks, pretty much what I was looking for. – elssar Dec 27 '12 at 11:44
  • Thanks - this will do for Flask apps on Heroku. Seems to be pretty much impossible to get Heroku's (Nginx-based) router to directly serve static content, unless it's in the `static` or `public` directory of a project. Would prefer, of course, to serve `robots.txt` and `sitemap.xml` directly with Nginx, in environments that let me do it. – Jaza Sep 30 '15 at 23:28
  • Correction to my above comment: looks like Heroku doesn't use Nginx at all, it actually passes all requests (including requests to the `static` or `public` dir) directly to an app process (e.g. directly to Gunicorn for a typical Python app), only going via a custom proxy / load-balancer called Vegur - see: http://superuser.com/questions/837925/what-is-vegur-appearing-in-http-headers – Jaza Sep 30 '15 at 23:45
  • Thank you, it is amazing solution. – Andrew G Jun 04 '18 at 18:58
12

Flask has built in support for serving static files.

Make a /static directory and put your files there. Then, when you instantiate Flask, specify the static_url_path parameter:

app = Flask(__name__, static_url_path='/')

The default is to serve static files from the /static/ path, but you want them served from / so they are where expected.

See the Flask API Docs for more info.

In addition to overhead and unnecessary code, the problem with your approach is if / when one of the files you want to serve contains something that looks like a template tag to render_template -- you can cause a rendering error. If you were to read the file into memory (once, not inside the method) then use that string as the body of the response without calling render_template, you would at least avoid that problem.

Jonathan Eunice
  • 21,653
  • 6
  • 75
  • 77
agf
  • 171,228
  • 44
  • 289
  • 238
  • You mean something like this - `robots= open(cwd+'/static/robots.txt', 'r').read()` and then `return robots` in the `robots_txt` function? – elssar Dec 27 '12 at 03:43
  • @elssar - I don't think you should reinvent file caching - check out http://packages.python.org/Flask-Cache/ – Alex L Dec 27 '12 at 04:32
  • @elssar Yes, that's what I mean, but I'm not suggesting you actually do that. I was just pointing out that if you were intending to read in and output static files, `render_template` wasn't doing just that. – agf Dec 27 '12 at 05:54
5

The best way is to set static_url_path to root url

from flask import Flask

app = Flask(__name__, static_folder='static', static_url_path='')
dns
  • 2,753
  • 1
  • 26
  • 33