Hardware FixRecommendedDevice not working? Your driver may be the problemCheck updates for common hardware issues.Fix DriversOctober DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsSlow PC?RecommendedPC slow today? Run a repair scan before it gets worseResolve common Windows issues and optimize system performance.Scan Now×
Skip to content
World desk11 min

How to Fix Nginx and Gunicorn Static Files Not Loading

A practical, end-to-end guide to fixing missing Django static files behind Nginx and Gunicorn. Learn how to collect assets, map STATIC_ROOT correctly with alias or root, resolve 404 and 403 errors, verify permissions, reload the active Nginx configuration and test delivery.
Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Static files should normally bypass Gunicorn in production: Django collects CSS, JavaScript, images and admin assets into STATIC_ROOT, Nginx serves that directory, and Gunicorn handles application requests. A missing file is therefore usually a collection, URL-prefix, Nginx path-mapping, permission or stale-configuration problem. Follow the checks below in order, starting with the exact asset URL that fails.

Understand the request path before changing configuration

With Django’s development server, runserver can locate and serve static assets while developing. A production Gunicorn process is an application server; it is not the component that should read every CSS, JavaScript or image file from disk. The production flow is:

As an Amazon Associate I earn from qualifying purchases.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  1. The browser requests a URL such as /static/css/site.css.
  2. Nginx matches the /static/ location and looks for a corresponding file in the collected static directory.
  3. If the file exists and permissions allow access, Nginx returns it directly.
  4. Requests that do not match a static file location are proxied to Gunicorn, which returns dynamic HTML or an application response.

If Nginx sends a static request to Gunicorn by mistake, the application may return a 404, perform unnecessary work, or apply application routing to a path that should have been a simple file lookup. Separating these two paths makes failures easier to identify.

Set Django’s static-file paths correctly

Use one collected directory

Set a URL prefix and a filesystem destination in the Django settings used by the production deployment:

#1 Best Overall
Sale
TOZO A1 Wireless Earbuds Bluetooth 5.3 Light Weight in Ear Headphones | IPX5 Waterproof,2 Mics for AI Calls,Immersive Premium Sound Bass Headset with Charging Case,32 EQ via App Black
  • [Ultra-Lightweight Ear Buds Designed for Small Ears] Each earbud weighs only 3.7g and features a compact, ergonomic in-ear design made especially for small ears. Secure, low-profile, and comfortable for workouts, all-day wear or overnight sleeping.The ultra-slim profile sits flush in your ear with no hard edges poking or rubbing, so you can even sleep on your side without discomfort.
  • [Immersive Stereo Sound with TOZO OrigX Technology] TOZO OrigX tuning delivers clear vocals, balanced mids, and natural stereo sound for music, podcasts, and videos.
  • [Long Battery Life for Daily Use] Get up to 7 hours of playtime on a single charge, with up to 32 hours total using the charging case—ideal for workdays, commuting, and extended listening sessions.
  • [Bluetooth 5.3 & Stable Connection] Bluetooth 5.3 provides fast pairing, stable wireless performance, and reduced dropouts as you move around home or office.
  • [Deep Bass with Clear Vocals] High-performance drivers produce punchy bass while keeping vocals clean and detailed for everyday listening.
STATIC_URL = "/static/"
STATIC_ROOT = BASE_DIR / "staticfiles"
# Optional source directory for project-level assets:
STATICFILES_DIRS = [BASE_DIR / "static"]

STATIC_URL is the browser-facing prefix. STATIC_ROOT is the directory where Django’s collection command places the files that a web server will publish. STATICFILES_DIRS is optional; it adds project-level source directories in addition to each installed app’s static directory.

Collect files after every deployment

Run the collection command from the project environment and directory that contain the production settings:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
python manage.py collectstatic --noinput

The command gathers files found by Django’s enabled static-file finders into STATIC_ROOT. It searches directories listed in STATICFILES_DIRS and the static directory of installed applications. If an application or source file was removed, stale files can remain in the destination; clear them deliberately with:

python manage.py collectstatic --clear --noinput

Use --clear only when the destination is dedicated to collected assets. It removes existing files in that collection directory before copying current files.

Confirm which source file Django selected

When a filename exists in more than one source directory, Django’s finder order determines which copy is collected. Check the selected file and the directories searched:

python manage.py findstatic css/site.css --verbosity 2

Replace the path with the relative name used by the template. The verbose output helps distinguish “the file was never discovered” from “the file was collected but Nginx cannot reach it.”

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Check the exact URL emitted by the page

Do not infer the failing path from a template filename. Open the rendered HTML, copy the complete href, src or image URL, and request that URL directly. A normal Django URL looks like:

Rank #2
Sale
Smart Watch for Men Women Compatible with iPhone Samsung Android Phone 1.69 inch Full Touch Screen IP68 Waterproof Bluetooh Fitness Tracker Heart Rate/Sleep Monitor (Black)
  • 【IP68 Waterproof Multifunctinal Smart Watch】Torjalph fitness activity tracker smart watch is IP68 waterproof technology ,you can wear it when swimming,handwashing,it's rain-proof which means that you perform multiple exercise modes outside without worrying about sweat or rain corroding your fitness smart watch,and this watch equipped with a 230 mAh large capacity battery magnetic charging ,and this sports activity tracker watch can be used 5-7 days If fully charged.
  • 【Larger 1.83'' Full Screen Watch & Multi-dial Selection】1.83 inch full HD color touch screen smart watch for men / women provides excellent 240*284 resolution image quality ,This Bluetooth smart watch offers a better touch experience and remarkable immersion and ,more than 200 watch faces for you to choose from App"Da Fit ", you can change the dial style according to your mood to wear keeping the world on your wrist
  • 【Accurate Activity Tracker Smart Watch 】sports watch activity tracker uses sweat and dust resistant breathable silicone strap, our Fitness Tracker Smart Watch for Android iOS Phones supports 12 sports modes,walking,running,bicycle,swimming,badminton,basketball,football,rope skipping. This bluetooth calls smart watch for men women is lightweight and stylish, bringing more convenient and relaxing sports experience while also providing more protection.
  • 【Your 24 Hour Personal Daily Health Monitor】Fitness watch for women men full touch screen IP67 waterproof bluetooth smartwatch for Android iOS Phones with heart rate blood pressure monitor multifunction Andriod smart watch with steps counter sleep monitor sports fitness watch, monitoring blood oxygen saturation allows you to understand your physical condition in real time to track fitness progress ,to know your health status in time.
  • 【More Functions Bluetooth Call Smart Watch】This fitness activity smart watch full screen for iPhone and Android phones includes a bunch of new features including:The bluetooth smart watch can hang up the phone, receive and display WhatsAPP /Text /Facebook /Twitter /Messenger reminder.Support sedentary reminder, alarm clock, stopwatch, timer,weather information,music control, remote camera control compatible with Android 5.0 and above or iOS 9.0 and above smartphones.
/static/css/site.css

Compare its prefix with STATIC_URL and with the Nginx location. A setting such as STATIC_URL = "/assets/" requires a matching location /assets/; an Nginx block for only /static/ cannot serve it. Also check that the page is not generating an old absolute URL, an HTTP URL from an HTTPS page, or a different hostname. Browser developer tools show the requested asset URL, response status and any mixed-content or host error.

Map /static/ to the collected directory in Nginx

Use alias when the location is the collected directory

Nginx’s alias replaces the matching location prefix with the configured path. If collected files are in /srv/myapp/staticfiles/, use:

server {
    server_name example.com;

    location /static/ {
        alias /srv/myapp/staticfiles/;
        access_log off;
        expires 7d;
    }

    location / {
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_pass http://unix:/run/gunicorn.sock;
    }
}

Keep the trailing slash on both location /static/ and alias /srv/myapp/staticfiles/. With this mapping, a request for /static/css/site.css is looked up as /srv/myapp/staticfiles/css/site.css.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Use root when the configured directory is the parent

root appends the complete request URI to the configured directory. For a request such as /images/example.png, root /data; searches /data/images/example.png. Therefore, if the collected directory is /srv/myapp/staticfiles/ and you want to use root, point it at the parent directory and preserve the URL hierarchy:

location /static/ {
    root /srv/myapp;
    try_files $uri =404;
}

Here, /static/css/site.css resolves to /srv/myapp/static/css/site.css, not to /srv/myapp/staticfiles/css/site.css. This is correct only when the files really exist under the resulting path. If your files are in a directory named staticfiles, alias is usually the more direct mapping.

Root versus alias at a glance

Directive How Nginx builds the filesystem path Example result for /static/css/site.css Use when
alias /srv/myapp/staticfiles/; in location /static/ Replaces the matching /static/ prefix /srv/myapp/staticfiles/css/site.css The location should map directly to the collected directory
root /srv/myapp; in location /static/ Appends the complete URI /srv/myapp/static/css/site.css The configured root is a parent containing a directory that matches the URL

The most common path error is combining a collected directory named staticfiles with a root value that makes Nginx search a different directory. Resolve one sample URL on paper before changing permissions or application code.

Return a clear 404 instead of proxying a missing asset

Add try_files to a static location when you want Nginx to verify that a file exists:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Rank #3
New Bee Guitar Cable 10ft Electric Instrument Bass AMP Cord
  • [Low Noise High Fidelity] New bee guitar cord with 22AWG oxygen-free-copper center conductors provides enhanced signal transfer, the PVC insulation material and the high-density stranded copper double shield provide noise rejection for quiet operation
  • [Ideal for Choice] New bee 1/4 straight and right angle metal connectors, ensures a secure connection in any instrument, pedal, or amp jack. L shape great fro on stage guitar stands
  • [Wide Compatible] Suitable for mixer, power amplifier, microphone, effector, decoder, equalizer, electoronic organ, electric guitar, electric bass and other electronic equipments. Widely used in the installation of smart home, karaoke speaker, professional stage audio, etc
  • [Easy Disassemble] Making incidental repairs quick and easy! Very nice, flexible cable
  • [Dont Hesite to Order] 24 hours customer services and professional technology team are standing by
location /static/ {
    alias /srv/myapp/staticfiles/;
    try_files $uri =404;
}

A missing asset then produces a direct 404, which points to collection, naming or path mapping. Do not put an unrelated proxy_pass in the same static location unless you have intentionally designed and tested that fallback. Forwarding a failed static lookup to Gunicorn can hide the original Nginx path mistake and produce an application-level 404 instead.

The dynamic location should remain separate and should contain the proxy headers and Gunicorn upstream:

location / {
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_pass http://unix:/run/gunicorn.sock;
}

Diagnose a 404 systematically

  1. Copy the exact browser URL. For example, use /static/css/site.css, not merely “site.css.”
  2. Check the destination file. Verify that the corresponding relative path exists under STATIC_ROOT, such as /srv/myapp/staticfiles/css/site.css.
  3. Ask Django whether it can find the source. Run python manage.py findstatic css/site.css --verbosity 2. If it cannot find the file, fix the source directory, installed app, filename or settings and rerun collection.
  4. Compare URL prefixes. The browser’s prefix, STATIC_URL and Nginx’s location must describe the same path.
  5. Resolve the Nginx path. For alias, replace the location prefix. For root, append the complete URI. Check spelling and case; Linux paths are case-sensitive.
  6. Look for a competing location. Another Nginx block, regular-expression location or included configuration can take precedence over the block you edited.

These checks separate a missing source file from a file that exists but is mapped incorrectly. Do not change the Gunicorn application until the filesystem path and Nginx location have been verified.

Fix 403 Forbidden and permission-denied errors

A 403 generally means Nginx found the path but its worker process cannot traverse a parent directory or read the file. Inspect the configured Nginx error log while making one request:

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
tail -f /var/log/nginx/error.log

The message identifies the path and often the access failure. The Nginx user needs execute permission on every parent directory from the filesystem root to the collected directory, plus read permission on the files. Grant only the access needed for the static tree. Do not make the entire project world-writable simply to make assets load; that expands the impact of a compromised process and can allow unwanted file changes.

Check directory traversal and file readability along the exact path reported in the log. If collection runs as one user but Nginx runs as another, ensure the resulting ownership and mode bits permit reading without exposing secrets, source code or deployment credentials. Keep private settings, virtual-environment files and application data outside the directory Nginx serves.

Use HTTP requests to distinguish the remaining failure types

After fixing paths and permissions, test one known asset from the public endpoint:

Rank #4
BSIMB 15.6" Digital Family Calendar & Chore Chart, No Subscription Fees
  • Digital Family Calendar: BSIMB digital calendar wall touch screen puts an end to your chaotic life. Designed for busy families, the digital calendar keeps you organised and in step with your family. Assign different colours and tasks to each family member to make family scheduling easier and more intuitive.
  • Easy Setup and Auto-Sync: Plug in, connect to WLAN and auto-syn your electric calendar in minutes. Digital wall calendar compatible with Google calendar, iCloud calendar, Outlook calendar, Cozi calendar and Yahoo calendar. Add events directly on the device or through the ecalendar app. Electronic calendar stays current whether you're at work or traveling—no manual updates needed.
  • All-In-One Home Assistant: Smart calendar builds habits and encourage independence with personalised chores. Display daily meal plan to reduce the daily ‘What's for dinner?’ stress. To-do lists keep track of everything you want. Turning chores into valuable achievements with stars and rewards, making life easier and more enjoyable.
  • Manage Anytime Anywhere: BSIMB electronic calendar for family stay connected with the powerful ecalendar app. Access and manage your digital wall calendar anytime, anywhere with free features like stars & rewards, photo screensaver, chore charts, meals plans, to-do lists etc. Electronic planner paid features like Magic Import tailor advanced tools for users with special needs.
  • Stay in Touch with Loved Ones: Share photos or videos from your phone to electronic wall calendar with app, instantly update your life moment, and keep in touch with your family, especially those who are far away; you can also surprise your smart calendar digital wall by sending him a wish card; it's also great for keeping tech-illiterate elders in touch with the youngsters.
curl -I https://example.com/static/admin/css/base.css
curl -sS https://example.com/static/admin/css/base.css -o /dev/null -w '%{http_code}n'

Interpret the result as follows:

  • 200: Nginx delivered the file. If the page still looks unstyled, inspect the HTML URL, browser cache and any additional asset requests.
  • 404: The file was not found at the mapped path, was not collected, or the request reached an application fallback instead of the intended static location.
  • 403: Nginx reached the location but cannot traverse or read it; use the error log and filesystem permissions.

Run the commands while watching both access and error logs:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
tail -f /var/log/nginx/access.log /var/log/nginx/error.log

The access log confirms which server handled the request and records the status; the error log supplies path and permission details.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Reload the Nginx instance that is actually serving traffic

Configuration edits have no effect until the active instance validates and reloads them:

sudo nginx -t
sudo systemctl reload nginx

If the result does not change, verify which configuration file the running binary was compiled to use:

nginx -V 2>&1 | tr ' ' 'n' | grep -- --conf-path

This exposes the compiled configuration path and catches edits made to an unused file. Also confirm that the server name and listening address in the edited server block match the hostname used by the request. A valid syntax test alone does not prove that the request selected the block you changed.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

When HTML loads but CSS or JavaScript does not

Inspect every failed request

Open the browser’s developer tools, reload the page and inspect the Network panel. Record the exact URL, status and response headers for each missing asset. A page can return 200 while one stylesheet, script or image still points to a wrong prefix or filename.

Check URL and scheme mismatches

A wrong STATIC_URL, an HTTP asset URL embedded in an HTTPS page, or a different host can prevent the browser from requesting the file that Nginx serves. Correct the generated URL rather than adding broad proxy rules that disguise it.

Verify hashed filenames

Storage backends that append content hashes produce names different from the original source filename. Confirm that the hashed name referenced in the rendered HTML is present in STATIC_ROOT. If the HTML references a name that collection did not create, rerun collection with the production settings and check the storage configuration that generated the references.

Rule out stale HTML and cache

If a deployment changed filenames or paths, an older cached HTML document can continue requesting assets that no longer exist. Request the asset directly, reload the page without the stale document and inspect response headers before changing Nginx routing.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Gunicorn’s role in static-file handling

Gunicorn should receive dynamic requests after Nginx has had the opportunity to serve a static file. A deployment pattern can check for a static file and proxy to the application only when no file is found, but an explicit Nginx static location is easier to audit because it makes the filesystem mapping visible. If every /static/ request appears in Gunicorn logs, inspect location matching and included Nginx files before changing Django views.

Do not rely on application routes to compensate for a missing collection step. The application can only return a file if a route and storage mechanism were deliberately configured for that purpose; it does not replace Nginx’s direct file delivery.

Compare deployment choices before changing architecture

Decision axis Local Nginx serving collected files CDN or object storage serving assets
URL-to-filesystem mapping Must be correct with root or alias Origin storage key and public URL must correspond
Collection destination One local STATIC_ROOT must be populated on the host serving requests Collection or deployment must upload the generated files to the remote origin
Permissions Nginx needs directory traversal and file-read access The delivery service needs access to the published objects while private application files remain protected
Caching and names Browser and Nginx cache headers must match the deployment’s update strategy CDN cache invalidation or immutable hashed names must match the deployment’s update strategy

Changing to a CDN or object store will not repair an incorrect STATIC_URL or uncollected files by itself. First establish that Django generates the intended names and that the deployment publishes those names to the selected origin.

A complete recovery sequence

Use this order when several symptoms appear at once:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  1. Open the page source and copy one failing asset URL.
  2. Confirm that STATIC_URL produces that prefix and that the production settings define STATIC_ROOT.
  3. Run python manage.py findstatic for the asset’s relative path with verbose output.
  4. Run python manage.py collectstatic --noinput; use --clear only when removing stale files is intended.
  5. Verify the resulting file under the actual collection directory.
  6. Choose either a direct alias mapping to that directory or a root mapping whose parent-and-URI calculation lands on it.
  7. Keep the static location separate from the Gunicorn proxy location and add try_files $uri =404; when a direct missing-file status is useful.
  8. Check parent-directory traversal and file-read permissions for the Nginx worker user.
  9. Run sudo nginx -t and reload the active Nginx service.
  10. Use curl, access logs and error logs to verify one asset returns the expected status.
  11. Only after direct delivery works, investigate browser cache, mixed content, hostnames or hashed filenames for assets that still fail in the page.

Deployment checklist

  • STATIC_URL matches the URL prefix emitted in HTML.
  • STATIC_ROOT is an explicit, writable collection destination during deployment.
  • collectstatic ran with the same settings and environment used by production.
  • findstatic --verbosity 2 locates each source asset that should be collected.
  • The Nginx static location maps to the real collection directory.
  • Trailing slashes are present on an /static/ location and its direct alias.
  • root is used only when appending the full URI produces the intended path.
  • Static requests are not unintentionally proxied to Gunicorn.
  • Nginx can traverse every parent directory and read published files.
  • The edited configuration is the active one, passes nginx -t and has been reloaded.
  • A direct curl request returns 200 for a known asset.
  • Hashed filenames referenced by templates exist in the published collection.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Leave a Reply

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

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

More from the Wire

  1. World desk4 min
    How to Spot an AI Voice Scam Before Sending MoneyDon’t rely on how a caller sounds. Pause, call back through a known number, and verify the emergency with another trusted person before sending money.
  2. Mountain View desk4 min
    Google’s SynthID Detector: How to Check AI-Generated Images, Video and AudioGoogle’s SynthID Detector looks for an embedded watermark in supported images, video and audio. Here is what its results do—and do not—show.
  3. Redmond desk20 min
    How to create a link to File or Folder in Windows 11Windows 11 gives you several ways to point to a file or folder without moving or duplicating it. You can create a desktop shortcut,…
Recommended PC Tool
Recommended PC Tool
Crashes, No Sound, or Screen Glitches?Free driver scan
PC Slower Than It Used to Be?Free scan - under a minute

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.