Chapter 3: Django – Collect Static Files

Collecting static files — the python manage.py collectstatic command.

This is the step that separates “it works on my laptop” from “it works on the internet”.

Many people reach deployment day, see 404 errors on CSS/JS/images/favicons, panic, and spend hours googling “Django static files not working Railway / Render / Heroku”.

Today I’m going to teach you exactly what collectstatic does, why you need it, how to run it properly, what happens behind the scenes, common traps, and how to test it so you never get surprised during real deployment.

We’ll do this very slowly, step-by-step, like pair-programming — I’ll explain every command, every setting, every folder, and we’ll test everything together.

Step 1 – Why Do We Need collectstatic At All?

In development (runserver):

  • Django automatically finds and serves static files from:
    • each app’s static/ folder
    • folders in STATICFILES_DIRS
  • No problem — magic dev server handles everything

In production (real web server):

  • Django does NOT serve static files (security + performance reasons)
  • A separate web server (nginx, Apache) or middleware (WhiteNoise) must serve them
  • All static files from every app + project folders must be copied into one single folder
  • That single folder is called STATIC_ROOT
  • collectstatic is the command that copies everything into STATIC_ROOT

Without collectstatic → production server sees 404 on every CSS/JS/image → site looks broken

Step 2 – Confirm Your Static Files Settings (Very Important)

Open mysite/settings.py

Make sure these lines exist (add them if missing):

Python

Important notes:

  • STATICFILES_DIRS → extra places Django searches in development
  • STATIC_ROOT → empty folder that collectstatic will fill
  • Never manually put files in STATIC_ROOT — collectstatic overwrites it

Step 3 – Create Some Static Files (If You Haven’t Already)

Quick example structure:

text

If you don’t have files yet, create minimal ones:

static/css/global.css:

CSS

static/images/logo.png → any small image (or placeholder text PNG)

Step 4 – Run collectstatic for the First Time

In terminal (project root):

Bash

You will see:

text

Type yes and press Enter.

Then:

text

What just happened?

  • Django searched:
    • STATICFILES_DIRS (static/)
    • every installed app’s static/ folder (polls/static/, pages/static/, etc.)
  • Copied all files into staticfiles/
  • Used the storage backend (CompressedManifestStaticFilesStorage if you set it) → added content hashes to filenames (cache busting) → compressed files (gzip/brotli)

Now look inside staticfiles/:

text

→ All files are now in one place — ready for production server.

Step 5 – Test in Development (WhiteNoise Serves Them)

If you have WhiteNoise configured (middleware + storage):

Restart server:

Bash

Visit /polls/ or any page:

  • Logo appears
  • Global CSS applied
  • App-specific CSS applied
  • Browser dev tools → files served from /static/… with 200 OK

WhiteNoise serves from STATIC_ROOT even in development — very realistic test.

Step 6 – Production Deployment Flow (When You’re Ready)

  1. Set DEBUG = False
  2. Set ALLOWED_HOSTS = [‘yourdomain.com’, ‘.railway.app’, …]
  3. Run python manage.py collectstatic –noinput (in deploy script / pipeline)
  4. Use a platform that supports Python + persistent filesystem:
    • Railway / Render / Fly.io → automatic
    • Heroku → needs Whitenoise + buildpack
    • VPS → nginx serves /static/ → /path/to/staticfiles/

WhiteNoise middleware handles serving automatically.

Step 7 – Common “Why 404?” Traps & Fixes

Problem Cause Fix
404 on static files after deploy Forgot to run collectstatic Run it every deploy
Files missing from staticfiles/ Forgot STATICFILES_DIRS or app static/ folder Check settings & folder structure
Old hashes / no cache busting Not using CompressedManifestStaticFilesStorage Set STATICFILES_STORAGE
CSS/JS not applying Forgot {% load static %} in template Add at top of every template that uses static
collectstatic asks for confirmation Normal first time Use –noinput in scripts

Your Quick Practice Task (Do This Right Now)

  1. Confirm STATICFILES_STORAGE and STATIC_ROOT in settings.py

  2. Create or verify at least one CSS file in static/css/ and one in polls/static/polls/css/

  3. Run:

    Bash
  4. Look inside staticfiles/ → see all files copied

  5. Restart server → check if styles/images load

  6. (Optional) Add a favicon:

    static/images/favicon.ico → <link rel=”icon” href=”{% static ‘images/favicon.ico’ %}”>

Tell me what feels next:

  • “Done! Now show me how to deploy to Railway / Render with WhiteNoise”
  • “How to handle user-uploaded images (MEDIA files) in production?”
  • “What is the difference between WhiteNoise and serving via nginx?”
  • “Got error during collectstatic – here’s message”
  • Or finally ready for Django Forms + Voting + POST + F() + results page

You now understand collectstatic completely — this one command is the difference between “broken site on internet” and “beautiful working app”.

You’re doing really well — let’s keep building! 🚀🇮🇳

You may also like...

Leave a Reply

Your email address will not be published. Required fields are marked *