If you’ve ever needed to exclude certain URLs from an HTTP-to-HTTPS redirect in Nginx especially when those URLs are dynamically generated by a backend like Django you’ve likely encountered confusing errors like 404 Not Found
or 504 Gateway Timeout
. Let’s dissect the problem and implement a clean solution while enhancing your Nginx configuration with practical improvements.
Understanding the Problem
Your goal is to allow two dynamically generated URLs (/this/endpoint1.txt
and /this/endpoint2.txt
) to be accessible over HTTP while redirecting all other traffic to HTTPS. The challenge arises because:
- Dynamic Content: These endpoints aren’t static files; they’re generated by Django via Gunicorn.
- Redirect Conflicts: Nginx’s
return 301
directive overrides proxy rules if not structured properly.
Your initial attempts failed because:
- Using
root
assumes the URLs map to physical files (they don’t). proxy_pass http://127.0.0.1:80
creates a loop (Nginx redirects to itself).
Targeted location
Blocks
To exclude specific URLs from the redirect:
- Define
location
blocks for the excluded URLs before the global redirect. - Proxy requests for those URLs directly to Gunicorn (bypassing the HTTPS redirect).
Here’s the revised configuration:
HTTP Server Block (Port 80)
server { listen 80; listen [::]:80; server_name mydomain.org; # Handle excluded endpoints location /this/endpoint1.txt { proxy_pass http://unix:/run/gunicorn.sock; # Match Gunicorn socket proxy_set_header Host $http_host; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-For $remote_addr; proxy_redirect off; } location /this/endpoint2.txt { proxy_pass http://unix:/run/gunicorn.sock; proxy_set_header Host $http_host; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-For $remote_addr; proxy_redirect off; } # Redirect all other HTTP traffic to HTTPS location / { return 301 https://$server_name$request_uri; } }
HTTPS Server Block (Port 443)
server { listen 443 ssl; listen [::]:443 ssl; server_name mydomain.org; # SSL and other settings (keep your existing config) ssl_certificate ...; ssl_certificate_key ...; # Serve Django via Gunicorn for all HTTPS requests location / { proxy_pass http://unix:/run/gunicorn.sock; proxy_set_header Host $http_host; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-For $remote_addr; proxy_redirect off; } # Static and media paths (keep your existing config) location /static/ { ... } }
Key Fixes Explained
- Order Matters:
Nginx processeslocation
blocks in order of specificity. By defining/this/endpoint1.txt
and/this/endpoint2.txt
before the global redirect, they’re excluded from the301
rule. - Proxy to Gunicorn Directly:
Usingproxy_pass http://unix:/run/gunicorn.sock
(orhttp://0.0.0.0:8000
if bound to a port) ensures requests for the excluded URLs are sent directly to Django, avoiding infinite loops. - Headers Consistency:
Replicatingproxy_set_header
directives ensures Django receives the same metadata for both HTTP and HTTPS requests.
Enhancing Functionality
Let’s add features to improve security and flexibility:
Rate Limiting for Excluded Endpoints
http { limit_req_zone $binary_remote_addr zone=excluded_endpoints:10m rate=5r/s; server { ... location /this/endpoint1.txt { limit_req zone=excluded_endpoints burst=10 nodelay; proxy_pass ...; } location /this/endpoint2.txt { limit_req zone=excluded_endpoints burst=10 nodelay; proxy_pass ...; } } }
Caching Dynamic Content
Cache responses for 5 minutes to reduce backend load:
location /this/endpoint1.txt { proxy_cache my_cache; proxy_cache_valid 200 5m; proxy_pass ...; }
Content-Type Enforcement
Force text/plain
for the endpoints:
location ~ \.txt$ { add_header Content-Type text/plain; proxy_pass ...; }
Final Thoughts
Final Thought
Excluding URLs from Nginx redirects hinges on prioritizing specificity in location
blocks and ensuring requests bypass the HTTPS rule by proxying directly to your backend (like Gunicorn). Always test with tools like curl
to confirm behavior, and enhance security with rate limiting or caching for high-traffic endpoints. By balancing precise routing with performance optimizations, you maintain a secure, efficient setup without compromising dynamic content needs.