Deploy static files in production

Hi all,

I am trying to deploy Django in production,
but I am having trouble with serving static files.

With DEBUG = True
and with
python3 manage.py runserver 127.0.0.1:33000 --insecure

the server works fine, as soon as I type
python3 manage.py collectstatic
the CSS stops working.

In settings.py I have the following configuration:
STATIC_URL = ‘static/’
STATIC_ROOT = BASE_DIR / ‘productionfiles’
STATICFILES_DIRS = [BASE_DIR / ‘static’,]

I even tried ‘whitenoise’, but this gave
me even more errors.

Whenever I try to run Django, it doesn’t include
the CSS and Javascript files.

Can anyone help me deploy Django in production
with serving static files?

Thanks in advance.

What are you using as your production webserver? Nginx? Apache? How are you running Django? Gunicorn? uwsgi?

Trying to use runserver as a production server is a very bad idea in almost every case.

I am using Apache and reverse proxy as a production server. I am running Django
normally from a virtual environment.

I am not trying to access Django through Apache, I just
typed python3 manage.py runserver 127.0.0.1:22000

Not even that is working

In an production environment, Django does not serve static files.
You run the manage.py collectstatic to have Django copy your static files from your project to the directories that are served by your file server. It is your file server, Apache in this case, that needs to be configured to serve the files from the urls defined by STATIC_URL from the locations identified by STATIC_ROOT.

Review the docs about deployment starting with How to deploy Django | Django documentation | Django. Also see How to manage static files (e.g. images, JavaScript, CSS) | Django documentation | Django

It is not appropriate and not supported to use runserver in production. It’s not what you use to run Django in that type of environment. You should be using uwsgi or gunicorn to run your application behind Apache.

You’ll need to be a lot more specific than that. Are you getting a traceback? If so, what? Are your requests getting 404s? If so, what do the requests look like?

“You’ll need to be a lot more specific than that. Are you getting a traceback? If so, what? Are your requests getting 404s? If so, what do the requests look like?”

I am not getting any type of errors, it is just that my site
is not displaying correctly because the static files like
css seems unavailable.

I already succeeded in serving static files on a “lab VPS”, but
for some reason it does not work online with the same configuration.

Blockquote Are you getting a traceback? If so, what? Are your requests getting 404s? If so, what do the requests look like?

I am getting some kind of error:
“GET /static/css/site-style.css HTTP/1.1” 404 2247

What does your Apache configuration look like for the /static url? Is it configured to serve files from your productionfiles directory? Does Apache have the appropriate permissions for that directory? What does your Apache log look like for those requests?

When I enter mydomain.com/static
I get 404 page with and without the
–insecure option

Blockquote Is it configured to serve files from your productionfiles directory?
No, in the configuration file I got…

Alias /static /home/…/…/project/mysite/mysite/static
<Directory /home/…/…/project/mysite/mysite/static>
Require all granted

Does the code snippet above have to point to productionfiles
instead of static?

I even installed libapache2-mod-wsgi-py3

I tried:

<Directory /home/…/…/project/mysite/mysite>

# <Files wsgi.py>
#    Require all granted
# </Files>

WSGIDaemonProcess mysite python-path=/home/…/…/project/mysite python-home=/home/…/…/
WSGIProcessGroup mysite
WSGIScriptAlias / /home/…/…/project/mysite/mysite/wsgi.py

Blockquote What does your Apache log look like for those requests?

GET /static HTTP/1.1" 404 2726

Yes, because of this:

From the docs for STATIC_ROOT:

The absolute path to the directory where collectstatic will collect static files for deployment.

This needs to be the same as what you have configured for Apache. And, the target directory needs to be able to be written by the uid being used to run the collectstatic command while Apache only needs read rights to that directory.

In the common case, and to avoid dealing with a bunch of permissions issues, it’s usually best to specify some directory in a location that Apache typically uses - perhaps something like /var/www/html/static.

Thanks, I changed the path to ‘productionfiles’ in
the apache2 configuration file and my website
is still being displayed the same way, without
css and js.

I tried a lot of guides and a lot of configurations
but none of them work.

How to Install Apache with Python Mod_wsgi on Ubuntu 20.04

How to Install Django with Apache on Ubuntu 22.04

… etc…

As soon as I set DEBUG=False, the css stops
displaying.

Please post your updated Apache configuration section for /static. Did you confirm that the Apache process has read access to that directory? Did you confirm that collectstatic copied the static files into that directory?

Please post your updated Apache configuration section for /static.

ProxyPreserveHost On

ProxyPass / http://127.0.0.1:8080/
ProxyPassReverse / http://127.0.0.1:8080/

Alias /static /home/../../project/mysite/productionfiles
<Directory /home/../../project/mysite/productionfiles>
    Require all granted
</Directory>

<Directory /home/../../project/mysite/mysite>
    <Files wsgi.py>
        Require all granted
    </Files>
</Directory>

WSGIDaemonProcess mysite python-path=/home/../../project/mysite python-home=/home/../../
WSGIProcessGroup mysite
WSGIScriptAlias / /home/../../project/mysite/mysite/wsgi.py

I found a partial solution to the problem.

I had to add a couple of lines to wsgi.py
from django.contrib.staticfiles.handlers import StaticFilesHandler
application = StaticFilesHandler(get_wsgi_application())

Now the css is working, my website is displaying
correctly but my pyscript scripts are not working.

Side note: When posting code, configuration files, etc here, enclose the contents of the files between lines of three backtick - ` characters. This means you’ll have a line of ```, then your code (template, error message, etc), then another line of ```. This forces the forum software to keep that file properly formatted. I have taken the liberty of making this correction on your latest post.

Yes, it works. No, it’s not the “solution” - it’s a band-aid and not something you want to use in a production environment.

If this is what you actually have within your configuration, this would not be correct. Please do not shorten or abbreviate information here when it needs to be validated by comparing it to other entries.

Also, you still have not answered the other two questions:

  • Did you confirm that the Apache process has read access to that directory?\
  • Did you confirm that collectstatic copied the static files into that directory?

Side note: When posting code, configuration files, etc here, enclose the contents of the files between lines of three backtick - ` characters.

Thanks for the advice and thanks for correcting my post. I didn’t know that you could correct my post, it looks much better.

Please do not shorten or abbreviate information here when it needs to be validated by comparing it to other entries.

I didn’t want to ‘advertise’ my website nor expose my username

Did you confirm that the Apache process has read access to that directory?\

I think so. I am not super good in linux nor English. Are you referring to the www-data user?
Now everything is displaying “correctly” so I suspect that Apache has read access
to that directory.

Did you confirm that collectstatic copied the static files into that directory?

Yes, the productionfiles is full of files.

Yes, it works. No, it’s not the “solution” - it’s a band-aid and not something you want to use in a production environment.

And I thought that I had 3 ways to deploy Django,
with Gunicorn, uWSGI and mod_wsgi. Do I really need
to use Gunicorn in a production environment, can’t
I create a service that runs Django in a virtual machine
and make the service start at boot instead of that?

I saw a lot of network diagrams that used Gunicorn, is
Gunicorn the best choice for production?

One of the duties / benefits of being a moderator.

If that is the user running the Apache instance, then yes.

At least those three - there are a couple options beyond that as well.

Actually, that’s how you would run gunicorn in a production environment - as a service running within the context of a virtual environment.

What you do not want to do in production is use runserver. That is a bad idea.

I wouldn’t necessarily say it’s the “best”, at least not in all circumstances. However, it’s probably the easiest.

We use uWSGI for a couple of different reasons, none likely to be relevent to you.

I haven’t used mod_wsgi for more than 10 years now, it’s just too restrictive for my taste. It being specific to Apache is the most significant reason. We moved all our web servers to nginx and are very happy we did.

What you do not want to do in production is use runserver. That is a bad idea.

I’ll remember that, so I have to use Gunicorn.

I haven’t used mod_wsgi for more than 10 years now, it’s just too restrictive for my taste. It being specific to Apache is the most significant reason. We moved all our web servers to nginx and are very happy we did.

I like Nginx too, but to use modsecurity for example with nginx
I have to compile nginx, compiling is very prone to
errors and it is not “scalable”, everytime I have to
update I think it will be a nightmare, because the
chances of errors are too big. (CIA…)

I wanted something that updates along with my
operating system and that could use modsecurity
without having to compile so I ended up using Apache.

Or uWSGI, or Daphne, or any other WSGI server that supports Django as an application.

I don’t know what Linux distribution you are using, but from what I’m seeing related to Debian / Ubuntu, you don’t need to compile nginx itself. You need to download the sources because you need to compile a module for nginx.

I don’t have any first-hand knowledge on this topic - if you have information to the contrary, please let me know!

(I don’t know what the steps might be if you’re using a RedHat derrivative, but I’d be surprised if it were significantly different.)

This statement is not accurate on both counts. (How do you think the distros build their packages from upstream sources? They compile them. That’s about as scalable as you can imagine when you consider the thousands of packages the major distros make available.)

You compile the connector module once, and if necessary, create an installable package for it.

It’s a question of what you’re needing to update, but calling it a “nightmare” is a bit of an overstatement. Yes it’s an extra step, but it’s hardly something to be scared of.

Any half-way decent software written for a reasonable Posix-like environment is designed to be compiled by an end-user. The idea of expecting your distro to supply everything you’re going to ever need is a relatively recent concept. There are still a number of niche areas where you’re expected to build your own software customized for your particular situation, because the distros don’t handle it.

Now having said all that, …

That’s a perfectly valid choice. There’s nothing wrong with avoiding steps in your build process.

Side note: See the announcement at F5 NGINX ModSecurity WAF Is Transitioning to End-of-Life - NGINX. This may also affect the long-term viability of ModSecurity with Apache as well - that’s a topic you may wish to keep your eyes on.

It’s a question of what you’re needing to update, but calling it a “nightmare” is a bit of an overstatement. Yes it’s an extra step, but it’s hardly something to be scared of.

In my opinion it feels like a nightmare because errors can occur during build
and the Continuity Integrity & Availability(CIA) will suffer. In IT they even
have names for periods that services are unavailable.

It looks like something for very tech savvy people and most
of these stuff are not an objective during certification exams.

I installed both gunicorn and uwsgi to test them out.

It feels like I am right at the beginning again when
the static files were not showing.

As soon as I set DEBUG to False, the static files stopped
showing in a brand new and unedited project by running:

python3 manage.py runserver

and

uwsgi --http localhost:8000 --wsgi-file mysite/wsgi.py

Now there is no ‘/etc/apache2/sites-enabled/xy.conf’ file
to include the static path in it.

I tried apply whitenoise, but that did not work either.

How can I make a brand new project display the static
file when DEBUG is set to False?

Should the static files be displayed with DEBUG=False
by running python3 manage.py runserver before
I can run uwsgi --http localhost:8000 --wsgi-file mysite/wsgi.py

There are lot’s of guides on youtube and on the
internet, but almost none of them work for me,
even if I do exactly what they are doing…

Only if you don’t verify the build before trying to deploy. The risk is no greater than any other part of the update process.

If you’re managing a deployment process, then you need to be tech-savvy.

As someone who has been deeply involved in interviewing and hiring developers for the past 25 years, I can tell you that I have never put any value in industry-based certifications. The presence (or absence) of a certification has never been a factor in the screening, interview, or hiring process.


If you are doing this:

Then you do not do this:

You do not use runserver in your production environment.

I don’t know what you’re trying to say by this.

Specific recommendations:

  • Create a directory named static in /var/www/html.
  • Ensure that the directory is owned by www-data
  • Set STATIC_ROOT=/var/www/html/static
  • Set your alias in your apache configuration for /static to refer to /var/www/html/static
  • Run manage.py collectstatic
  • Verify that the files were collected in /var/www/html/static
  • Verify that the collected files are readable by www-data.
  • Restart the apache process
  • Restart the uwsgi process
  • Identify some specific static file within the collected files, and try to access it directly from the browser.
    • For example, if you have a file /var/www/html/static/css/main.css, try to access http://<whatever hostname>/static/css/main.css