Skip to content

Admin Panel Static Files Fix

Problem

The admin panel was experiencing 404 errors when loading images in production. The errors showed requests to URLs like:

  • https://tracker-admin.glimpse.technology/static/images/d3f3e4-ccd-4e7a-bd1c-1c29fc657.png

These requests were failing with 404 errors, causing a loop of failed image loading attempts.

Root Cause

The issue had multiple components, with the primary root cause being AWS infrastructure misconfiguration:

  1. AWS Target Group Misconfiguration (Primary): The load balancer target group was pointing to port 8200 (frontend React app) instead of port 8000 (API server with static files)
  2. Missing AWS Listener Rule: No listener rule existed to route /static/* requests to the API server
  3. Incorrect image URL generation: The API was returning relative URLs instead of full URLs pointing to the API server
  4. Missing nginx proxy rule: The admin panel's nginx configuration was missing a proxy rule for /static/ requests (less relevant for Cloudflare + AWS ELB setup)

How the System Works

  1. API Server: Serves static files (images) at /static/images/ endpoint via FastAPI's StaticFiles mount
  2. Admin Panel: Built with Vite/React, served by nginx in production
  3. Image URLs: The API returns image URLs with paths like /static/images/filename.png
  4. Expected Flow: Admin panel requests → nginx proxy → API server static files

What Was Missing

The admin panel's nginx configuration had proxy rules for:

  • /api/ requests (proxied to API server)
  • Various specific API endpoints

But it was missing a proxy rule for /static/ requests, so these requests were being handled by nginx's default file serving, which couldn't find the files (since they exist on the API server, not in the admin panel's static files).

Solution

The fix involved two changes:

1. Added nginx proxy rule for static files

Added a new nginx location block in tracker-admin/nginx.conf to proxy static file requests to the API server:

#                                                            Handle static file requests (images, etc.)
location /static/ {
    #                                                        Proxy to the API server for static files
    proxy_pass                                               __API_URL__/static/;
    proxy_http_version                                       1.1;
    proxy_set_header                                         Host tracker.glimpse.technology;
    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 "https";

    #                                                        SSL configuration for proxy (if using HTTPS)
    proxy_ssl_server_name                                    on;
    proxy_ssl_protocols                                      TLSv1.2 TLSv1.3;

    #                                                        Cache static files for longer
    expires                                                  7d;
    add_header                                               Cache-Control "public, max-age=604800";
}

2. Updated API to return full URLs

Modified the get_full_url function in app/api/routes/images.py to return complete URLs pointing to the API server:

def get_full_url(url_path: str) -> str:
    """
    Generate a full URL for an image.

    Args:
        url_path: The relative URL path of the image

    Returns:
        The full URL including the external base URL
    """
    # For production, construct full URL pointing to the API server
    # This ensures images are loaded from the correct domain
    if url_path.startswith('/'):
        # Remove leading slash since EXTERNAL_BASE_URL should include the protocol and domain
        url_path = url_path[1:]

    return f"{settings.EXTERNAL_BASE_URL}/{url_path}"

3. Updated configuration and CORS settings

  • Added production domains to CORS settings in app/core/config.py
  • Set EXTERNAL_BASE_URL environment variable in compose.yml for production
  • This ensures the API returns URLs like https://tracker.glimpse.technology/static/images/filename.png

4. Fixed AWS Load Balancer Configuration

Root Cause Discovered: The target group was pointing to the wrong port (8200 - frontend) instead of the API server (8000).

AWS Infrastructure Fix:

# Updated target group to point to correct API server port
aws elbv2 deregister-targets \
  --target-group-arn arn:aws:elasticloadbalancing:eu-west-2:951665295205:targetgroup/glimpse-tracker-api/56e6f6c5ec49a604 \
  --targets Id=i-022d1ec1b8c62660b,Port=8200

aws elbv2 register-targets \
  --target-group-arn arn:aws:elasticloadbalancing:eu-west-2:951665295205:targetgroup/glimpse-tracker-api/56e6f6c5ec49a604 \
  --targets Id=i-022d1ec1b8c62660b,Port=8000

# Simplified health check for better reliability
aws elbv2 modify-target-group \
  --target-group-arn arn:aws:elasticloadbalancing:eu-west-2:951665295205:targetgroup/glimpse-tracker-api/56e6f6c5ec49a604 \
  --health-check-path "/" \
  --health-check-interval-seconds 10 \
  --health-check-timeout-seconds 5 \
  --healthy-threshold-count 2

Key Features of the Fix

  1. Correct Target Group: Routes requests to API server (port 8000) instead of frontend (port 8200)
  2. AWS Listener Rule: Routes /static/* requests to the API server target group
  3. Proxy Configuration: Routes /static/ requests to the API server
  4. SSL Support: Includes proper SSL configuration for HTTPS
  5. Caching: Sets appropriate cache headers for static files (7 days)
  6. Headers: Forwards necessary headers for proper request handling

Deployment

After making these changes, both the API and admin panel containers need to be rebuilt and redeployed:

# Rebuild both containers
docker-compose build api admin

# Restart both services
docker-compose restart api admin

# Or restart the entire stack
docker-compose down && docker-compose up -d

Important: Make sure the EXTERNAL_BASE_URL environment variable is set correctly in your production environment to point to your API server (e.g., https://tracker.glimpse.technology).

Verification

SOLUTION VERIFIED AND WORKING:

  1. Image requests return 200 OK: Static files serve correctly from https://tracker.glimpse.technology/static/images/
  2. Images load properly: Admin panel Image Gallery displays images without errors
  3. Network requests successful: Browser network tab shows 200 status codes for all static file requests
  4. No more looping 404s: The infinite retry loop has been completely eliminated

Test Results:

curl -I "https://tracker.glimpse.technology/static/images/86a045390c76406fbc0b68a146c7335f.png"
HTTP/2 200
content-type: image/png
content-length: 6490

Admin Panel Status: ✅ Images loading correctly in production

Development vs Production

Development

  • Vite dev server handles proxying via vite.config.ts
  • Both /api/v1 and /static are proxied to the dev container
  • No nginx involved in development

Production

  • nginx serves the built admin panel
  • nginx proxies both /api/ and /static/ requests to the API server
  • This fix ensures consistency between dev and production behavior

Subsequent Issue: Frontend Routing

⚠️ Important Note: After implementing this fix, a subsequent issue occurred where the frontend application became inaccessible. This was because the AWS load balancer changes made the main domain point only to the API server, breaking frontend routing.

Issue: https://tracker.glimpse.technology returned 405 Method Not Allowed errors instead of serving the frontend.

Solution: Additional AWS load balancer configuration was required to properly route different URL paths to different services.

📖 See: Frontend Routing Fix Guide for the complete solution to restore frontend functionality while maintaining static file serving.

  • tracker-admin/nginx.conf - Main nginx configuration (fixed)
  • tracker-admin/vite.config.ts - Development proxy configuration
  • app/main.py - API server static file mounting
  • app/api/routes/images.py - Image API endpoints
  • docs/guides/frontend-routing-fix.md - Follow-up fix for frontend routing

Prevention

To prevent similar issues in the future:

  1. Test Static Files: Always test image/file uploads and display in production-like environments
  2. Test All Services: When modifying AWS load balancer configuration, test all application services (frontend, admin, API)
  3. Proxy Consistency: Ensure nginx proxy rules match Vite dev proxy rules
  4. Documentation: Document all proxy requirements when adding new static file endpoints
  5. Monitoring: Set up monitoring for 404 errors on static file requests
  6. Staging Environment: Test AWS infrastructure changes in staging before production