skip to Main Content

I use Django/Gunicorn/Nginx stack for my small webapp. I managed to put the app to production except that my app shows static images correctly but does not find the css file.

My static resources like images, css, js were collected by python manage.py collectstatic outside of the project folder, to a folder accessible by Nginx: /var/www/example.com/html/static/. The content of the folder:
`

├── admin
│   ├── css
│   │   ├── autocomplete.css
│   │   ├── ....
│   ├── img
│   │   ├── calendar-icons.svg
│   │   ├── ....
│   └── js
│       ├── SelectBox.js
│       ├── ....
├── booking
│   ├── css
│   │   └── style.css
│   ├── img
│   │   ├── andromeda.webp
│   │   ├── ....
├── img
│   └── favicon_16.ico
└── ksa .....

`

My index page refers to images and css in the booking app. Here are two examples:

<!--It is located in the head section-->
<link rel="stylesheet" href="{% static 'booking/css/style.css' %}">

<!--It is located in the body section-->
<img src="{% static 'booking/img/andromeda.webp' %}" alt="">

As you can see the references are following the same structure and consistent with the real folder structure. That’s why it is weird that images return 200 status code while css returns 404. When I check the generated html, it also seems good. The generated html snippets:

<!--It is located in the head section-->
<link rel="stylesheet" href="/static/booking/css/style.css">

<!--It is located in the body section-->
<img src="/static/booking/img/andromeda.webp" alt="">

Here is my relevant part of my django setting file:

from dotenv import load_dotenv
import os

load_dotenv()

from pathlib import Path

# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = False

ALLOWED_HOSTS = ['example.com', 'www.example.com','localhost']

# Application definition

INSTALLED_APPS = [
    'booking.apps.BookingConfig',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles'
]

SESSION_COOKIE_SECURE=True
CSRF_COOKIE_SECURE=True

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

ROOT_URLCONF = 'ksa.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

WSGI_APPLICATION = 'ksa.wsgi.application'

STATIC_ROOT = '/var/www/example.com/html/static/'
STATIC_URL = 'static/'

MEDIA_URL = 'media/'
MEDIA_ROOT = '/var/www/example.com/html/uploads/'

Finally, here is my Nginx settings:

server {
    server_name example.com www.example.com;

    location /favicon.ico { access_log off; log_not_found off; }
    location /static/ {
        alias /var/www/example.com/html/static/;
    }
    location /media/ {
        alias /var/www/example.com/html/uploads/;
    }
    location / {
        include proxy_params;
        proxy_pass http://unix:/run/gunicorn.sock;
    }
    
    #No browser side cache for html and css
    location ~* .(?:html|css){
    expires -1;
    }

    #1 week browser side cache for images
    location ~* .(?:jpg|jpeg|gif|png|ico|webp){
    expires 1w;
    add_header Cache-Control "public";
    }

    error_page 404 /404.html;
    location /404.html {
        root /var/www/example.com/html;
        internal;
    }


    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

}

server {
    if ($host = example.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot

    server_name example.com;
    listen 80;
    return 404; # managed by Certbot
}

So, do you have any idea, how can it happen that css is not found although it is referred exactly the same way as the images?

UPDATE:
Checking the access log, none of the static resources are accessible:

65.154.226.169 - - [13/Feb/2024:07:42:45 +0100] "GET /static/booking/css/style.css HTTP/1.1" 404 197 "https://example.com/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.5938.132 Safari/537.36"

65.154.226.169 - - [13/Feb/2024:07:42:45 +0100] "GET /static/booking/img/fokep.webp HTTP/1.1" 404 197 "https://example.com/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.5938.132 Safari/537.36"

The error log of Nginx shows more interesting fact:

2024/02/13 07:42:45 [error] 508095#508095: *3407 open() "/usr/share/nginx/html/static/booking/css/style.css" failed (2: No such file or directory), client: 65.154.226.169, server: example.com, request: "GET /static/booking/css/style.css HTTP/1.1", host: "example.com", referrer: "https://example.com/"

2024/02/13 07:42:45 [error] 508095#508095: *3410 open() "/usr/share/nginx/html/static/booking/img/andromeda.webp" failed (2: No such file or directory), client: 65.154.226.169, server: example.com, request: "GET /static/booking/img/andromeda.webp HTTP/1.1", host: "example.com", referrer: "https://example.com/"

So, now it is trivial why static resources are not served. Still I do not understand from where Nginx takes this path, if the configuration file for Nginx includes:

location /static/ {
        alias /var/www/example.com/html/static/;
    }

No any reference on /usr/share/nginx/html/static/

2

Answers


  1. Chosen as BEST ANSWER

    I applied a brute force approach to find the solution. I tried all the potential combination for the nginx config file and the django settings.py file.

    It was the working combination: Relevant snippet from settings.py:

    STATIC_ROOT = '/var/www/example.com/html/static/'
    STATIC_URL = 'static/'
    
    MEDIA_URL = 'media/'
    MEDIA_ROOT = '/var/www/example.com/html/uploads/'
    

    Relevant snippet from the webs server configuration file

    server {
        ...
        
        location /static {
            alias /var/www/example.com/html/static;
        }
        
        location /media {
            alias /var/www/example.com/html/uploads;
        }
        location / {
            include proxy_params;
            proxy_pass http://unix:/run/gunicorn.sock;
        }
    ...
    

  2. Add a slash as starting char for the strings MEDIA_URL and STATIC_URL.

    STATIC_ROOT = '/var/www/example.com/html/static/'
    STATIC_URL = '/static/'  # old 'static/'
    
    MEDIA_URL = '/media/'  # old 'media/'
    MEDIA_ROOT = '/var/www/example.com/html/uploads/'
    

    Without the leading slash your path or source (src="") is relative, meaning it refers to the current url you are on right now.
    When you open http://www.domain.com/books/authors/ and have

    <img src="{% static 'images/myimage.png' %}" alt="My Image">
    

    Then the url would resolve to http://www.domain.com/books/authors/static/images/myimage.png

    But your nginx conf is configured in a way that you want absolute paths and not relative paths.

    What’s weird to me is that you did not report issues with the media files. What’s also weird, that the total paths that you show do indeed have a leading slash like so:

    <!--It is located in the body section-->
    <img src="/static/booking/img/andromeda.webp" alt="">
    

    Here, according to your stated STATIC_URL settings, should be no leading slash… Confusing.

    If this does not work make sure to always run collectstatic after making changes to STATIC_ROOT. Always reload the nginx conf after making changes to it (obvious I guess).

    If the error still persists I would like to see the nginx error log files.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search