ThisCalculator

Everyday

Fix Nginx & Gunicorn Static Files Not Loading

Updated · Standard formula, shown below

Fix Nginx & Gunicorn Static Files Not Loading

When you run a Django or Flask app behind Gunicorn and Nginx, static files (CSS, JavaScript, images) often fail to load, leaving your site unstyled or broken. This guide walks you through the most common causes and exact fixes, from Nginx configuration to file permissions and Django settings.

What You’ll Need

  • Access to your server via SSH (root or sudo privileges)
  • Your project’s directory path (e.g., /var/www/myproject)
  • Your Nginx configuration file location (usually /etc/nginx/sites-available/)
  • Basic familiarity with the command line

Step-by-Step

  1. Check your Django/Flask static settings.
    For Django, ensure in settings.py you have:

    STATIC_URL = '/static/'
    STATIC_ROOT = '/path/to/your/project/staticfiles/'

    For Flask, set static_folder='static' in your app. Then run python manage.py collectstatic (Django) to gather all static files into STATIC_ROOT.

  2. Verify the static files actually exist.
    Run ls -la /path/to/your/project/staticfiles/ (or your static folder). If empty, you missed collectstatic or your app’s static files are elsewhere.
  3. Check file permissions.
    Nginx runs as user www-data (on Debian/Ubuntu). Ensure that user can read the static files:

    sudo chown -R www-data:www-data /path/to/your/project/staticfiles/
    sudo chmod -R 755 /path/to/your/project/staticfiles/

    If your project is in your home directory, Nginx may not have access. Move it to /var/www/ or adjust permissions.

  4. Edit your Nginx server block.
    Open your site’s config (e.g., /etc/nginx/sites-available/myproject) and add a location block for static files. It must be outside the location / block that proxies to Gunicorn:

    server {
        listen 80;
        server_name example.com;
    
        location /static/ {
            alias /path/to/your/project/staticfiles/;
        }
    
        location / {
            proxy_pass http://127.0.0.1:8000;
            proxy_set_header Host $host;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
    }

    Important: Use alias and ensure the trailing slashes match. If you use root instead, the path must include the static directory name.

  5. Test Nginx configuration.
    Run sudo nginx -t. If it reports an error, fix the syntax (common: missing semicolon or mismatched braces).
  6. Reload Nginx.
    Run sudo systemctl reload nginx (or sudo service nginx reload).
  7. Clear your browser cache and test.
    Hard refresh (Ctrl+Shift+R) or open an incognito window. Visit http://yourdomain.com/static/css/style.css – you should see the file content, not a 404 or a Django error page.

Common Mistakes

  • Missing trailing slashes – In the alias directive, the path must end with a slash, and the location must also have a slash. Mismatches cause 404s.
  • Using root instead of alias – With root /path/to/staticfiles/, Nginx looks for /path/to/staticfiles/static/..., which is usually wrong.
  • Forgetting to run collectstatic – If you changed your static files, you must run it again.
  • Permissions on parent directories – Even if the static folder is readable, Nginx needs execute permission on every parent directory (e.g., /home/user/project). Use sudo chmod o+x /home /home/user /home/user/project or move the project.
  • Nginx serving Django’s 404 – If you see a Django error page for static files, it means Nginx is proxying /static/ to Gunicorn. Your location block is missing or misconfigured.

Tips

  • Use a dedicated domain or CDN for static files in production (e.g., static.example.com) to reduce server load.
  • Enable gzip compression for static assets in Nginx:
    gzip on;
    gzip_types text/css application/javascript image/svg+xml;
  • Set long cache headers for static files:
    location /static/ {
        alias /path/to/staticfiles/;
        expires 30d;
        add_header Cache-Control "public";
    }
  • If you use a virtual environment, ensure Gunicorn runs with the correct Python path – but that’s unrelated to static files.

Troubleshooting

If static files still don’t load after following the steps:

  • Check Nginx error logs: sudo tail -f /var/log/nginx/error.log. Look for “Permission denied” or “open()” errors – these point to permission or path issues.
  • Check access logs: sudo tail -f /var/log/nginx/access.log to see if requests hit Nginx and what status code they return.
  • Test the file directly: curl -I http://localhost/static/css/style.css (on the server). If you get 200, the issue is browser cache or DNS. If 404, check the path.
  • Verify Nginx user has access: Run sudo -u www-data ls /path/to/staticfiles/. If it fails, permissions are wrong.
  • Check for SELinux (on RHEL-based systems): sudo setenforce 0 temporarily to test. If that fixes it, set the proper context: sudo chcon -R -t httpd_sys_content_t /path/to/staticfiles/.
  • If you use a Unix socket for Gunicorn, ensure the socket file is accessible to Nginx – but again, static files are handled by Nginx directly, so this is only relevant for dynamic pages.

Frequently Asked Questions

Why do static files work in development but not production?

In development, Django/Flask serve static files themselves. In production, Nginx should serve them directly. If you haven’t configured Nginx to do so, it proxies to Gunicorn, which may not serve static files efficiently or at all.

What is the difference between alias and root in Nginx?

root appends the location URI to the root path. For example, root /var/www/static; with a request to /static/css/style.css looks for /var/www/static/static/css/style.css. alias replaces the location part, so alias /var/www/static/; looks for /var/www/static/css/style.css. Use alias for static files.

Do I need to run collectstatic every time I change a CSS file?

Yes, if you’re using Django’s STATIC_ROOT. collectstatic copies all static files from your apps and STATICFILES_DIRS to STATIC_ROOT. Without it, Nginx won’t see the updated files.

Can I serve static files with Gunicorn?

Technically yes, but it’s inefficient and not recommended. Gunicorn is for Python web applications, not static file serving. Nginx is far better at handling concurrent static requests.

Why do I get a 403 Forbidden error?

This usually means Nginx can’t read the file due to permissions. Check that the www-data user has read and execute permissions on the directory and all parent directories.

What if my static files are in a different location than the default?

Adjust the alias path to point to your actual static directory. Also update STATIC_URL in Django if you’re using a custom URL prefix.

Related

Method: the standard formula for this calculation, rounded as each result states. See how the calculators work or report an error.