Configure static files to work with NGINX

Hi everyone,

I’d like to have your general rule of thumb for handling static files within a Django project in order to get along NGINX.

Here is how I’m used to work :

settings.py

STATIC_URL = "/src/" # Django will search for /src/
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, "src"), # Django will search for /src/
]
STATIC_ROOT = "static/"# Basic configuration when using manage.py collectstatic

My issue is that Nginx doesn’t get files present in the ~/static/ folder. But I did use

python3 manage.py collecstatic

Any idea ?

Likely a permissions issue. From the conf fragment in your previous thread:

location /static/ {
        root /home/theo/mywebsite;
    }

Couple items:

You don’t want to retrieve static files from your home directory - you want to move the static files into a directory that nginx is going to have access to.

STATIC_ROOT should be the full path from which the files are being retrieved

You’ve got STATIC_URL set to /src/, but your static location in nginx is /static/

Hi @KenWhitesell,

You don’t want to retrieve static files from your home directory - you want to move the static files into a directory that nginx is going to have access to.

Well I agree with you but according to DigitalOcean, one should set Nginx like this :

server {
    listen 80;
    server_name server_domain_or_IP;

    location = /favicon.ico { access_log off; log_not_found off; }
    location /static/ {
        root /home/sammy/myprojectdir;
    }

    location / {
        include proxy_params;
        proxy_pass http://unix:/run/gunicorn.sock;
    }
}

But I think that you are right because I now get a 403 forbidden nginx page when I try to access my css files :

http://192.168.1.69/static/css/layout.css

image

So it’s obvious that it is a permission issue, nginx actually doesn’t seem to have “authorization” to serve /home/theo/mywebsite/static/ (yet it can serve the html files, I don’t understand why ?)

Here is what I get after doing :

> cat /var/log/nginx/error.log

2020/12/15 11:27:20 [error] 436#436: *1 open() "/home/theo/Blog/static/css/layout.css" failed (13: Permission denied), client: 192.168.1.52, server: 192.168.1.69, request: "GET /static/css/layout.css HTTP/1.1", host: "192.168.1.69", referrer: "http://192.168.1.69/"
2020/12/15 11:27:20 [error] 436#436: *3 open() "/home/theo/Blog/static/css/fonts.css" failed (13: Permission denied), client: 192.168.1.52, server: 192.168.1.69, request: "GET /static/css/fonts.css HTTP/1.1", host: "192.168.1.69", referrer: "http://192.168.1.69/"
2020/12/15 11:27:20 [error] 436#436: *4 open() "/home/theo/Blog/static/css/app_base/navbar.css" failed (13: Permission denied), client: 192.168.1.52, server: 192.168.1.69, request: "GET /static/css/app_base/navbar.css HTTP/1.1", host: "192.168.1.69", referrer: "http://192.168.1.69/"
2020/12/15 11:27:20 [error] 436#436: *5 open() "/home/theo/Blog/static/css/app_base/articles.css" failed (13: Permission denied), client: 192.168.1.52, server: 192.168.1.69, request: "GET /static/css/app_base/articles.css HTTP/1.1", host: "192.168.1.69", referrer: "http://192.168.1.69/"
2020/12/15 11:27:20 [error] 436#436: *6 open() "/home/theo/Blog/static/css/app_base/footer.css" failed (13: Permission denied), client: 192.168.1.52, server: 192.168.1.69, request: "GET /static/css/app_base/footer.css HTTP/1.1", host: "192.168.1.69", referrer: "http://192.168.1.69/"
2020/12/15 11:27:27 [error] 436#436: *6 open() "/home/theo/Blog/static/css/fonts.css" failed (13: Permission denied), client: 192.168.1.52, server: 192.168.1.69, request: "GET /static/css/fonts.css HTTP/1.1", host: "192.168.1.69"
2020/12/15 11:27:31 [error] 436#436: *6 open() "/home/theo/Blog/static/css/app_base/articles.css" failed (13: Permission denied), client: 192.168.1.52, server: 192.168.1.69, request: "GET /static/css/app_base/articles.css HTTP/1.1", host: "192.168.1.69"
2020/12/15 11:27:42 [error] 436#436: *6 open() "/home/theo/Blog/static/css/layout.css" failed (13: Permission denied), client: 192.168.1.52, server: 192.168.1.69, request: "GET /static/css/layout.css HTTP/1.1", host: "192.168.1.69"
2020/12/15 11:51:02 [error] 436#436: *8 open() "/home/theo/Blog/static/css/app_base/navbar.css" failed (13: Permission denied), client: 192.168.1.52, server: 192.168.1.69, request: "GET /static/css/app_base/navbar.css HTTP/1.1", host: "192.168.1.69"
2020/12/15 11:51:06 [error] 436#436: *8 open() "/home/theo/Blog/static/css/layout.css" failed (13: Permission denied), client: 192.168.1.52, server: 192.168.1.69, request: "GET /static/css/layout.css HTTP/1.1", host: "192.168.1.69"
2020/12/15 11:51:38 [error] 436#436: *8 open() "/home/theo/Blog/static/css/layout.css" failed (13: Permission denied), client: 192.168.1.52, server: 192.168.1.69, request: "GET /static/css/layout.css HTTP/1.1", host: "192.168.1.69"

I can’t address what they’ve written, I can only share what I’ve done and used.

What HTML files are you referring to here? If you’re talking about your application-rendered templates, nginx isn’t serving files, it’s retrieving data from the app.

So according to you, should I move my whole project into /var/www or a special folder which would not be in /home/theo ?

Nope, not what I said at all.

There are two separate and independent operations going on here.

  1. Nginx receives a request for a url starting with /static/. Because it’s associated with a root directive, nginx attempts to open the associated file and return it to the requesting device.

  2. Nginx receives a request for something else. Because it’s associated with a proxy_pass directive, it forwards the request itself through the defined socket to whatever is listening on the other end (in this case, gunicorn). Nginx itself makes no attempt to access any file associated with what is listening to the socket.

Nginx needs no rights to any file that is part of the application.

What I wrote:

That’s all.

1 Like

Nginx receives a request for a url starting with /static/ . Because it’s associated with a root directive, nginx attempts to open the associated file and return it to the requesting device.

That’s the thing I don’t understand, if I have specified that it should be opened as root, then why does my static folder is not served ?

The directive root is “root” as in the root of a directory tree, not “root” as in the user.

Nginx doesn’t run as root. It starts as root in order to allocate port 80, but then reduces its UID to whatever account it’s configured to use.

You are right, Nginx is just the proxy guy.

The issue is directly in the project itself (My Blog app) because I tried to create a side basic-project and it worked perfectly with CSS.

So to debug, I did the following :

> sudo systemctl stop nginx # Stop Nginx service to test the project on its own with gunicorn
> cd ~/Blog/ # CD in project's directory
> source Blogenv/bin/activate # Activate the virtualenv
> (Blogenv): gunicorn --bind 192.168.1.15:8000 Blog.wsgi # Running a gunicorn instance directly binded to an IP and project's WSGI

Not found: /static/css/layout.css # I do have a static/css/layout.css file in the project's tree

It seems that the issue is here :

settings.py

STATIC_URL = "/static/" # Django will search for /static/
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, "static"), # Django will search for /static/
]
STATIC_ROOT = "static/" # Basic configuration when using manage.py collectstatic

If I comment STATIC_ROOT, it works by running :

> python3 manage.py runserver 192.168.1.15:8000

But not with gunicorn.

I don’t understand why

Do you have debug=True in your settings file? If you do, then runserver will search through your project directory to find static files.

Yes, it is in debug mode, is this why it works with manage.py runserver and not with gunicorn ?

Yes, because with debug on, runserver retrieves the files from within the project and not (just) from the static root. If you turn debug off, you should see the same behavior with both.
See runserver, particularly the section on the staticfiles app.

Have you tried changing the permissions on your static folder as a test?

Something like:
sudo chmod -R 644 /path/to/static/dir

If that works, also check which posix user is configured in your nginx.conf.

sudo nano /etc/nginx/nginx.conf
# Truncated contents of /etc/nginx/nginx.conf.

...
user ec2-user;  <------- HERE
...
server {
    listen 80;
    server_name example.com;

    location = /favicon.ico { access_log off; log_not_found off; }
    location /static/ {
        root /home/ec2-user/myproject;
    }

    location / {
        include proxy_params;
        proxy_pass http://unix:/home/ec2-user/myproject/myproject.sock;
    }
}
...
sudo nano /etc/nginx/proxy_params
# Contents of /etc/nginx/proxy_params

proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;

Restart NGINX and test.

The above is an example of what I am using currently on my personal blog, using NGINX, Gunicorn and Django.

I have the same problem, static files don`t work. What was the solution for this question?

If you’re having a similar issue to this, go ahead and open a new topic to address it. Include a description of your deployment environment - including your webserver settings (apache or nginx as appropriate), your STATIC related settings, DEBUG setting, whether or not you’ve verified that collectstatic has copied the static files to the right directory, and a description of the directory structure for static files.

Hi, refer to this article I have written to properly deploy your static files on a NGINX Production for Django. I am still using this exact procedure till date and it works like a charm.

https://wolfx.io/how-to-serve-static-and-media-files-in-nginx

Always whitenoise comes to the rescue.