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:
- 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)
- Missing AWS Listener Rule: No listener rule existed to route
/static/*requests to the API server - Incorrect image URL generation: The API was returning relative URLs instead of full URLs pointing to the API server
- 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
- API Server: Serves static files (images) at
/static/images/endpoint via FastAPI'sStaticFilesmount - Admin Panel: Built with Vite/React, served by nginx in production
- Image URLs: The API returns image URLs with paths like
/static/images/filename.png - 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_URLenvironment variable incompose.ymlfor 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
- Correct Target Group: Routes requests to API server (port 8000) instead of frontend (port 8200)
- AWS Listener Rule: Routes
/static/*requests to the API server target group - Proxy Configuration: Routes
/static/requests to the API server - SSL Support: Includes proper SSL configuration for HTTPS
- Caching: Sets appropriate cache headers for static files (7 days)
- 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:
- Image requests return 200 OK: Static files serve correctly from
https://tracker.glimpse.technology/static/images/ - Images load properly: Admin panel Image Gallery displays images without errors
- Network requests successful: Browser network tab shows 200 status codes for all static file requests
- 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/v1and/staticare 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.
Related Files
tracker-admin/nginx.conf- Main nginx configuration (fixed)tracker-admin/vite.config.ts- Development proxy configurationapp/main.py- API server static file mountingapp/api/routes/images.py- Image API endpointsdocs/guides/frontend-routing-fix.md- Follow-up fix for frontend routing
Prevention
To prevent similar issues in the future:
- Test Static Files: Always test image/file uploads and display in production-like environments
- Test All Services: When modifying AWS load balancer configuration, test all application services (frontend, admin, API)
- Proxy Consistency: Ensure nginx proxy rules match Vite dev proxy rules
- Documentation: Document all proxy requirements when adding new static file endpoints
- Monitoring: Set up monitoring for 404 errors on static file requests
- Staging Environment: Test AWS infrastructure changes in staging before production