pythonflaskfastcgi

Flask: Static files in subdirectories


In my flask template file I include a css file (I ommited the boilerplate) like this:

url_for('static', filename='css/bootstrap.css')

This renders to /static/css/bootstrap.css which means (because of leading slash) it is interpreted as domain.com/static/css/boostrap.css. Unfortunately the actual static folder is located a subdirectory: domain.com/projects/test/static/

Environment specifics:

My fcgi file located in the ~/fcgi-bin folder (host specific i guess):

$ cat ~/fcgi-bin/test.fcgi
#!/usr/bin/env python2.7

import sys
sys.path.insert(0, "/home/abcc/html/projects/test")

from flup.server.fcgi import WSGIServer
from app import app

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)

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

and my .htaccess located in domain.com/projects/test/

$ cat .htaccess 
<IfModule mod_fcgid.c>
   AddHandler fcgid-script .fcgi
   <Files ~ (\.fcgi)>
       SetHandler fcgid-script
       Options +FollowSymLinks +ExecCGI
   </Files>
</IfModule>

<IfModule mod_rewrite.c>
   Options +FollowSymlinks
   RewriteEngine On
   RewriteCond %{REQUEST_FILENAME} !-f
   RewriteRule ^(.*)$ /fcgi-bin/test.fcgi/$1 [QSA,L]
</IfModule>

To sum it up I want url_for('static', filename='css/bootstrap.css') to return static/css/bootstrap.css instead of /static/css/bootstrap.css

EDIT #1

I have noticed this happens also on normal url_for calls not for static files like url_for('about').

EDIT #2

I have written a quickstart-app and a blog post about this.


Solution

  • Dont set the script name to a blank string, try setting it to '/projects/test'. By setting SCRIPT_NAME to a blank string the application thinks its running on the root of the domain, so routes generated by url_for will start there. With a script name of '/projects/test', url_for('static', filename='/foo/bar.css') will return '/projects/test/static/foo/bar.css'

    If you genuinely want your apps static media served on a different endpoint then you'll just have to roll it yourself, something like this:

    from flask import Flask
    from os.path import join
    
    app = Flask(__name__)
    
    def url_for_static(filename):
        root = app.config.get('STATIC_ROOT', '')
        return join(root, filename)