2

I know there are tons of similar questions like this, but I couldn't find any that fits my case:

I'm using django (2.1.7) with gunicorn and nginx in a single docker container (requirement for the infrastructure we use at work), but have trouble with the admin menu. When executing locally and visiting http://localhost:8080/admin/ the page is rendered without css.

After a bit of digging I found out that nginx is, in fact, serving the files. For instance, I can visit http://localhost:8080/static/admin/css/base.css in my browser and the file in question is loaded. Also the static dir in my BASEDIR contains all the expected files...

I don't know how I can continue investigating this further. All related questions seem to have a flaw in their STATIC_ROOT config, forgot the collectstatic command or misuse the alias in the nginx config. All of which seem to work for me...

I feel like I'm missing something pretty basic. Does anyone of you have an idea? Thanks for your help!

Below my config files: settings.py:

"""
Django settings for mysite project.

Generated by 'django-admin startproject' using Django 2.1.7.

For more information on this file, see
https://docs.djangoproject.com/en/2.1/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/2.1/ref/settings/
"""

import os

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))


# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/2.1/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'u8$jv5m9+^pcpcjz$a!uipnq-ufu(kjwfq9ft2)no^-bqv&%$='

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = bool(int(os.environ["DEBUG"]))

ALLOWED_HOSTS = ["*"]


# Application definition

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

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 = 'mysite.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 = 'mysite.wsgi.application'


# Database
# https://docs.djangoproject.com/en/2.1/ref/settings/#databases

DATABASES = {
    "default": {
        "ENGINE": os.environ["DB_ENGINE"],
        "NAME": os.environ["DB_NAME"],
        "USER": os.environ["DB_USER"],
        "PASSWORD": os.environ["DB_PASSWORD"],
        "HOST": os.environ["DB_HOST"],
        "PORT": os.environ["DB_PORT"],
        "ATOMIC_REQUESTS": True,
    }
}


# Password validation
# https://docs.djangoproject.com/en/2.1/ref/settings/#auth-password-validators

AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]


# Internationalization
# https://docs.djangoproject.com/en/2.1/topics/i18n/

LANGUAGE_CODE = 'en-us'

TIME_ZONE = 'UTC'

USE_I18N = True

USE_L10N = True

USE_TZ = True


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.1/howto/static-files/

STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, "static")

gunicorn_config.py:

bind = '0.0.0.0:5000'
workers = 2
chdir = "./mysite"
reload = True

nginx.conf:

http {

    server {
        listen 8080;

        location /static/ {
            alias /code/mysite/static/;
            expires max;
        }

        location / {
            proxy_pass  http://127.0.0.1:5000;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header Host $host;
            proxy_redirect off;
        }
    }
}
events {

}

docker command: (can also post the whole Dockerfile and docker-compose if needed)

python ./mysite/manage.py makemigrations;
python ./mysite/manage.py migrate;
python ./mysite/manage.py collectstatic --noinput;
service nginx start;
gunicorn --config gunicorn_config.py mysite.wsgi:application
4
  • 1
    I hope after this question to change the SECRET_KEY :) :) May I ask if you open the browser Network Tab and click on the base.css file, does is show the contents (in the Response Tab)? Commented Feb 11, 2019 at 19:42
  • no worries, that's just a dummy key ;) Commented Feb 11, 2019 at 19:56
  • Yes indeed, the base.css is loaded (sorry for my earlier comment, I had a filter activated in my network tab). When I click on it I also see the content as well. Besides base.css I also see login.css and responsive.css Commented Feb 11, 2019 at 20:07
  • Alright, it seems like the css files are sent with a Content-Type "text/plain" instead of "text/css". I assume this is a setting issue in nginx. Can't figure out yet how though.. Commented Feb 11, 2019 at 20:49

1 Answer 1

9

Alight, thanks to nik_m's answer I managed to find the solution. The problem was, that the css files were served, but send as text/plain (which you can see in the Content-Type of the response headers).

The solution was to include mime.types in the nginx.conf similar to how it's shown here

This is my new nginx.conf:

http {
    include /etc/nginx/mime.types;

    server {
        listen 8080;

        location /static/ {
            alias /code/mysite/static/;
            expires max;
        }

        location / {
            proxy_pass  http://127.0.0.1:5000;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header Host $host;
            proxy_redirect off;
        }
    }
}
events {

}

Using the full mime.types from the nginx documentation and storing it at /etc/nginx/mime.types

Note: Don't forget to perform a hard refresh when reloading the page with cmd+shift+R ;)

Sign up to request clarification or add additional context in comments.

4 Comments

Very glad you figured it out! Accept your answer then. Great job!
Thanks for this! I've had this problem for the last year and now poof, it's gone.
OMG, you saved my day!! it took me forever to figure it out @.@
THANK GOD FOR YOU

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.