Deploying and running Django apps on Heroku is really a no-brainer, except for one thing — serving static files via collectstatic.

I run collectstatic as usual, using heroku run command just like how I did it for syncdb. It worked but I’m getting 404 error when serving static files.

It turns out that running collectstatic via heroku run spins up a new dyno and collectstatic is running in an isolated environment. So, all the collected static files are deployed to that location and only for this new dyno and the dyno attached to our Django app can’t access that. — Heroku support staff


The dead simple solution would be to run collectstatic as part of the Procfile before starting the app. We need to “chain” together our Procfile commands for the web process, like so:

OK there you go, no more 404 error when serving static files from your Django app on Heroku. Plus, every time you deploy your app, newly added static files will be collected automatically.


There are a lot of questions about the configurations. Cross check with my here

Important thing here is your STATICFILES_DIRS. Make sure to include your project_name/app_name/static here. In my case, I have project_name/staticfiles for the STATIC_ROOT. Change STATIC_URL = ‘/static/’ if you want to serve from Heroku, same goes to ADMIN_MEDIA_PREFIX = ‘/static/admin/’

Finally add this to your in order to serve from Heroku.

urlpatterns += patterns('',
 (r'^static/(?P.*)$', 'django.views.static.serve', {'document_root': settings.STATIC_ROOT}),

Files could be access as such:

/app/project_name/staticfiles/style.css >
/app/lib/python2.7/site-packages/django/contrib/admin/media/css/login.css >

Please note that this is not the best way to serve static files but it works. One last thing, you still need to go through all these things if you are using django storages and django compressor to serve static files via Amazon S3 because django compressor needs a temp file system cache on Heroku.

42 Replies to “Managing Django Static Files on Heroku

  1. I’ve just been looking into Heroku for hosting myself… I had read that the Cedar filesystem was ephemeral, i.e. wiped whenever the dyno is restarted. I thought that would preclude serving Django’s static files.

    But do your commands above automatically run collectstatic when the new dyno is spun up, to reinstate them?

    So it would just be user-uploaded files I’d need to run from S3 instead?

    • Sorry for the late, late reply.

      It will run every time you do git push heroku master.

      FYI, I’m using django storages & django compressor to serve static files via Amazon S3. In order for compressor to work, you’ll need a temp file system cache on Heroku, thus the solution above.

  2. Hi there, am running into the same problem.

    I wonder how you’ve setup your STATIC_URL, STATIC_ROOT and STATICFILES_DIR as I’m almost hitting my head on the wall now 🙂

  3. Thanks 🙂 It’s working now. Founded solution.

    So I have:
    In Debug:

    STATIC_ROOT = ‘static/’
    STATIC_URL = ‘/static/’
    and in
    urlpatterns = patterns(”,
    #### urls urls urls… 😉
    + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)

    And it’s working great 🙂

      • Even with Kamil’s writeup, I’m not sure what you’re proposing here. Sure, running collectstatic gets all the files in one place.

        But have you configured gunicorn to serve that directory separate from the Django staticfiles app?

        (If you’re willing to run the staticfiles app with DEBUG on, there’s not even a need to collectstatic; it seems to serve things fine from wherever they are. But that’s “probably insecure”, says the Django docs. So I’m trying to figure out if what you Kamil is describing somehow gets heroku-nginx or gunicorn to serve the collectedstatic directory…)

        • With more research I’m guessing you’ve each settled on some variant like this in

          if not settings.DEBUG:
          # screw the vague FUD and django doctrine against non-DEBUG static-serving
          urlpatterns += patterns(”,
          (r’^static/(?P.*)$’, ‘django.views.static.serve’, {‘document_root’ :settings.STATIC_ROOT}),

          The comments for ‘django.views.static.serve’ include more of the usual admonitions about how one ‘SHOULD NOT’ do this in a ‘production setting’. But these similarly-worded-but-never-explained warnings have echoed across Django comments/docs so much they’re beginning to look like superstitions to me rather than actual best practices.

  4. Thanks for this post! It was very helpful in getting django static files on heroku running.

    However, I’ve noticed that every time I deploy a change, even without adding any new static files, collectstatic has to run every time and causes the app restart to be longer. This is sort of annoying because any users on the site would experience a long response if they made a request while this process was happening. Do you have any advice for this problem?

    • This is because the collectstatic command is part of the Procfile. Quick solution will be creating two versions of Procfile, one with the collectstatic command.

      I can’t think of other cleaner solution.

  5. Nice post but I have a problem. I can’t get this done. I’m getting:

    unknown specifier: ?P.

    My looks like:


    urlpatterns = patterns(”,


    if not settings.DEBUG:
    urlpatterns += patterns”,
    (r’^static/(?P.*)$’, ‘django.views.static.serve’, {‘document_root’: settings.STATIC_ROOT}),

  6. Hi! Thank you for the blog post. However, I encountered this issue when running ‘foreman start’

    13:59:40 web.1 | started with pid 2060
    13:59:40 web.1 | Usage: collectstatic [options]
    13:59:40 web.1 |
    13:59:40 web.1 | Collect static files in a single location.
    13:59:40 web.1 |
    13:59:40 web.1 | error: no such option: –noinput;
    13:59:40 web.1 | process terminated
    13:59:40 system | sending SIGTERM to all processes

    I used this line in my Profile:
    web: python collectstatic –noinput; gunicorn_django –workers=4 –bind=$PORT

    It seems to be an issue with chaining the commands together on one line…any idea?

  7. Thanks for the post!! Question– how would I handle serving just a handful of static files on Heroku, and the majority via my main backend on Amazon S3? There are just a few files that I need on Heroku (to be on the same domain name… it’s related to browser compatibility issues), but I still want the rest of my files to be served via S3.
    Any suggestions on how to go about this?

  8. So what’s stopping you from committing the changed static files after running a collectstatic locally? Seems to work for me so far.

    In my I have:

    PROJECT_DIR = os.path.dirname(__file__)

    Then I use that in and to set the static directory… (Rather than a path starting in / as the manual insists on using…)

    STATIC_ROOT = os.path.join(PROJECT_DIR, ‘static’)

    for example…

  9. Also, the procfile didn’t work. I got it working with:

    web: python collectstatic –noinput; gunicorn -b$PORT -w 4 [project name].wsgi:application

    [project name] is the folder containing

    The following was enough if I committed the static files:
    web: gunicorn -b$PORT -w 4 [project name].wsgi:application

  10. Sorry but after updating your to serve the static files doesn’t that mean that every static file requested will have to be served by both the Dyno and the Django app instead of just the Dyno.
    This will push extra load on your app and that is the reason why it is marked as not good for production in the docs.

  11. A Tip for Django Peeps on Heroku

    #!/usr/bin/env python2.7
    # -*- coding:utf-8 -*-

    # Copyright 2013
    from django.http import HttpResponse, HttpResponseRedirect
    from django.shortcuts import render_to_response
    from django.template import RequestContext
    from django.conf import settings # Application Configuration

    # correct spacing
    def displayStaticFile(request, file):

    template = ‘x/’ + str(file)
    context = {}

    return render_to_response(template, context, context_instance=RequestContext(request))

    In your templates folder create an “x” folder and store your files in here… add this to your urls

    # Static Files
    url(r’^x/(?P.*)$’, ‘module.views.staticFileLoader.displayStaticFile’),

Leave a Reply

Your email address will not be published. Required fields are marked *