3

I use Docker Compose along with this Dockerfile that copies the static folder into /static:

FROM python:3
ENV PYTHONUNBUFFERED 1
RUN mkdir /code
WORKDIR /code
COPY requirements.txt /code/
RUN pip install --upgrade pip && pip install -r requirements.txt
COPY static /static/
COPY . /code/

And in my settings files I use:

if env == "dev":
    DEBUG = True
else:
    DEBUG = False
    SECURE_CONTENT_TYPE_NOSNIFF = True
    SECURE_BROWSER_XSS_FILTER = True
    X_FRAME_OPTIONS = "DENY"

STATICFILES_FINDERS = [
    'django.contrib.staticfiles.finders.FileSystemFinder',
    'django.contrib.staticfiles.finders.AppDirectoriesFinder',
]

STATICFILES_DIRS = [
    # os.path.join(BASE_DIR, "static/"),
    '/static'
]

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

The static files are working in dev but when I change the env to prod, I start getting 404 errors.

So you see the problem ?

4m1nh4j1
  • 4,289
  • 16
  • 62
  • 104
  • 1
    Django's static files do not work in production (https://docs.djangoproject.com/en/2.2/howto/static-files/#serving-static-files-during-development) you better configure nginx/apache/... for that. – Willem Van Onsem Jul 24 '19 at 09:00
  • 2
    See for example https://docs.djangoproject.com/en/2.2/howto/static-files/deployment/ – Willem Van Onsem Jul 24 '19 at 09:01
  • `STATICFILES_DIRS` is the location of your static files __inside your code repository__, where Django should fetch them from for `collectstatic`. So that setting is wrong, it should not be `/static` (usually, if inside a 'static' folder in your app, you don't need set this at all). Also show us your apache/nginx config. – dirkgroten Jul 24 '19 at 09:07
  • Note: I've written a [blog post](https://www.dedi.co/blog/entries/2018/12/17/deploying-static-files-aws-django-part-1) on how to setup static files with whitenoise and a CDN (targeted for AWS but contains useful explanations that help you understand how this whole thing works). – dirkgroten Jul 24 '19 at 09:08

2 Answers2

4

Note: This answer is also correct but I accepted the above answer too (since my app will receive very low traffic.)

Thanks for your comments. They were useful.

This is my new docker compose file:

version: '3.5'

services:
  nginx:
    image: nginx:latest
    ports:
      - "8002:8000"
    volumes:
      - $PWD:/code
      - ./config/nginx:/etc/nginx/conf.d
      - ./static:/static
    depends_on:
      - web
    networks:
      - my_net
  web:
    build: .
    command: python manage.py runserver 0.0.0.0:8000
    env_file: .env
    volumes:
      - $PWD:/code
      - ./static:/static
    expose:
      - "8000"
    networks:
      - my_net

networks:
  my_net:
    driver: bridge

And this is the Nginx conf file:

upstream web {
  ip_hash;
  server web:8000;
}

server {

    location /static/ {
        autoindex on;
        alias /static/;
    }

    location / {
        proxy_pass http://web/;
    }
    listen 8000;
    server_name localhost;
}

You should also add "web" to the allowed hosts:

ALLOWED_HOSTS = ['0.0.0.0', 'localhost', 'web']

Update: settings.py file:

STATICFILES_FINDERS = [
    'django.contrib.staticfiles.finders.FileSystemFinder',
    'django.contrib.staticfiles.finders.AppDirectoriesFinder',
]

STATICFILES_DIRS = [
    os.path.join(BASE_DIR, "static"),
    # '/static'
]

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

Also as @dirkgroten said you can set an expiry header in the static files your serve.

Another solution would be using Whitenoise (Thanks Daniel Roseman).

4m1nh4j1
  • 4,289
  • 16
  • 62
  • 104
  • For completeness, show your settings (STATIC_ROOT should be '/static' and STATICFILES_DIRS should be os.path.join(BASE_DIR, "static") or nothing if your static files are in your app's static dir) – dirkgroten Jul 24 '19 at 09:26
  • Also, as mentioned in my blog post, if you serve your static files using nginx, you should set expiry headers so users don't have to download them for every page. And you should use a different STATICFILES_STORAGE to add a manifest. – dirkgroten Jul 24 '19 at 09:28
3

While it is **strongly discouraged* to serve static files from Django in production (and for VERY good reasons), I often need to anyway.

In some cases its perfectly acceptable (low traffic, REST API only server, etc). If you need to do that, this snippet should help out. Adjust the re_path or use url() if thats your django flavor.

from django.contrib.staticfiles.views import serve as serve_static

def _static_butler(request, path, **kwargs):
    """
    Serve static files using the django static files configuration
    WITHOUT collectstatic. This is slower, but very useful for API 
    only servers where the static files are really just for /admin

    Passing insecure=True allows serve_static to process, and ignores
    the DEBUG=False setting
    """
    return serve_static(request, path, insecure=True, **kwargs)

urlpatterns = [
    ...,
    re_path(r'static/(.+)', _static_butler)
]

Andrew
  • 8,322
  • 2
  • 47
  • 70