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
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:
Relevant snippet from the webs server configuration file
Add a slash as starting char for the strings
MEDIA_URL
andSTATIC_URL
.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
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:
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 toSTATIC_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.