Meditations on programming, startups, and technology
New Relic

Serving Django Static Files through Apache

Django’s development server is capable of serving static (media) files thanks to the view django.views.static.serve. Popular web servers like Apache, Lighttpd or NGINX are much faster though, and as such should be used in production mode. Our goal is to bypass Django and let Apache (or other valid alternatives) directly serve static files like images, videos, CSS, JavaScript files, and so on, for us.

Generally speaking, for performance reasons, it’s advised that you have two different webservers serving your dynamic requests and static files. In practice, for smaller sites, people often opt to simply use one webserver. In this article, I’ll discuss how to serve the static files within your Django project, through Apache.

The first thing we need to do is distinguish between development and production mode. We can do so by simply specifying DEBUG = True (development), or DEBUG = False (production) within our settings.py file.

settings.py may include (among others) the following declarations:

# Absolute path to the project directory
BASE_PATH = os.path.dirname(os.path.abspath(__file__))

# Main URL for the project
BASE_URL = 'http://example.org'

DEBUG = False

# Absolute path to the directory that holds media
MEDIA_ROOT = '%s/media/' % BASE_PATH

# URL that handles the media served from MEDIA_ROOT
MEDIA_URL = '%s/site_media/' % BASE_URL

# URL prefix for admin media -- CSS, JavaScript and images.
ADMIN_MEDIA_PREFIX = "%sadmin/" % MEDIA_URL

*PATH constants indicate paths on your filesystem (e.g., /home/myuser/projects/myproject), while *URL constants indicate the actual URL needed to reach a given page or file.

Notice that it’s not unusual to have a /site_media URL that corresponds to a /media folder. In the example above, I opted to separate regular media files for the project from the standard ones that ship with Django for the admin section. To do this, all we have to do is create a symbolic link as follows:

ln -s /usr/lib/python2.5/site-packages/django/contrib/admin/media /path/to/myproject/media/admin

When you’re in development mode, and DEBUG = True, you want to let Django serve your static files. This can be done by adding the following snippet (or similar) to your urls.py:

if settings.DEBUG:
    urlpatterns += patterns('',
        (r'^site_media/(?P<path>.*)$', 'django.views.static.serve', {'document_root': settings.MEDIA_ROOT}),
    )

In production mode, the code contained within the if clause will not be executed as we’ve set DEBUG to False within settings.py.

From the Django side of things, we are good. We now need to instruct Apache. Within your virtual host file, you can specify something along the lines of:

<VirtualHost *:80>
 
  #...
 
  SetHandler python-program
  PythonHandler django.core.handlers.modpython
  SetEnv DJANGO_SETTINGS_MODULE myproject.settings
  PythonDebug On
  PythonAutoReload Off
  PythonPath "['/usr/lib/python2.5/site-packages/django', '/path/to/myproject'] + sys.path"
 
  #...
 
  Alias /site_media "/path/to/myproject/media"
  <Location "/site_media">
    SetHandler None
  </Location>
</VirtualHost>

The first group of declarations essentially tells Apache to use mod_python to handle any incoming requests. However, we don’t want Django to deal with static files, so the second group of declarations, aliases/maps the /site_media URL with the actual media directory on the server, and tells Apache to threat it as static content (with SetHandler None) bypassing de facto Django.

No related posts.


If you enjoyed this post, then make sure you subscribe to my Newsletter and/or Feed.

receive my posts by email

13 Responses to “Serving Django Static Files through Apache”

  1. Nice to see this piece of information in such a clear way.

    Tks for the tip.

  2. leonel says:

    You can try http://www.cherokee-project.com it’s an amazing webserver ..

  3. Yes, Leonel. Cherokee is awesome.

  4. sorl says:

    I fail to see the point in the “if settings.DEBUG” statement in urls.py since the webserver will already be taking care of things before you get there in production…
    Also “MEDIA_ROOT = ‘%s/media/’ % BASE_PATH” should probably be “MEDIA_ROOT = os.path.join(BASE_PATH, ‘media’)” if you want to be os agnostic.

  5. sorl, if Apache is misconfigured, checking for settings.DEBUG ensures that the files aren’t being served by Django. Regarding your second point, this article is clearly aimed at *nix systems (you wouldn’t do ln -s on Windows either).

  6. sorl says:

    Ironically if you are unsure if your production environment is set up properly you might want to set DEBUG to True. Perhaps using a similar “trick” to http://www.djangosnippets.org/snippets/644/ for urls is a better idea (import local_urls…)? As for point #2: There are absolutely no reasons not to use os.path.join

  7. sorl, while I don’t think it’s a big deal, I agree that using os.path.join is probably better.

  8. sorl says:

    I totally agree its not a big deal.. its just annoying to see that if statement since it doesn’t do much good, do you know any other blog posts I can spam? :)

  9. Katja says:

    Does it also work this way when I want to display pictures in MEDIA_ROOT on a webpage?
    I’ve tried this and don’t find the error – but don’t see a picture … *sigh*

    In the webpage I have:
    <img src="
    http:///zooscan/profilepics/PIC.png ” />
    Thanks for any hints!

  10. Greg Ferrell says:

    Thanks so much! This was just what I was looking for. Django is awesome, but this is its one Achilles heel so far.

  11. Thanks so much, Antonio!

  12. billy bob says:

    Thanks so much – been trying to get apache to serve static by itself for so long; most of the recipies are so complicated. This one is simples! Bookmarking this recipie though you may want to use mod_WSGI?

  13. Mike says:

    I had thought it was my apache cfg but after changing my django settings.py the way you have, presto – voila it worked! Many thanks!

Leave a Reply

I sincerely welcome and appreciate your comments, whether in agreement or dissenting with my article. However, trolling will not be tolerated. Comments are automatically closed 15 days after the publication of each article.

Copyright © 2005-2012 Antonio Cangiano. All rights reserved.