CORS

Fix CORS With a Reverse Proxy — Nginx in Front of Node or Python

Updated April 2026

Reading this article? Verify your fix in real-time. Test your reverse proxy CORS config → CORSFixer

If Nginx sits in front of your Node.js or Python backend, CORS headers can be added at two levels — Nginx or the application. Adding them at both causes duplicate headers. Here is exactly where to put them.

The duplicate header problem

Browser Console
The 'Access-Control-Allow-Origin' header contains multiple values '*, https://app.com', but only one is allowed.

This happens when both Nginx and your application add Access-Control-Allow-Origin. The browser sees two values and rejects both.

Option A — CORS only at Nginx (recommended)

Remove CORS middleware from your application and let Nginx handle it:

# nginx.conf
server { listen 443 ssl http2; server_name api.yourapp.com; location / { # Handle OPTIONS preflight at Nginx — app never sees it if ($request_method = OPTIONS) { add_header Access-Control-Allow-Origin "https://yourapp.com"; add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS"; add_header Access-Control-Allow-Headers "Authorization, Content-Type"; add_header Access-Control-Max-Age 86400; add_header Content-Length 0; return 204; } # Add CORS to all responses from the backend add_header Access-Control-Allow-Origin "https://yourapp.com" always; proxy_pass http://127.0.0.1:3000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; }
}

Then in your Node.js/Python app — remove all CORS middleware. Nginx handles it.

Option B — CORS only at the application

Keep CORS in your app and tell Nginx not to add it:

# nginx.conf — plain reverse proxy, no CORS headers
location / { proxy_pass http://127.0.0.1:3000; proxy_set_header Host $host; # No add_header CORS directives here
}
# Node.js — add CORS in app
app.use(cors({ origin: "https://yourapp.com" }));

# FastAPI — add CORS middleware
app.add_middleware(CORSMiddleware, allow_origins=["https://yourapp.com"])

Why duplicates happen

Nginx's add_header adds headers in addition to headers the upstream (your app) sends. It does not replace. If your app sends Access-Control-Allow-Origin: * and Nginx adds Access-Control-Allow-Origin: https://yourapp.com, the browser sees both and rejects them.

The proxy_hide_header workaround

# If you cannot remove CORS from the app:
# Strip app CORS headers at Nginx, then re-add correct ones
location / { proxy_hide_header Access-Control-Allow-Origin; proxy_hide_header Access-Control-Allow-Methods; proxy_hide_header Access-Control-Allow-Headers; proxy_hide_header Access-Control-Allow-Credentials; add_header Access-Control-Allow-Origin "https://yourapp.com" always; # etc. proxy_pass http://127.0.0.1:3000;
}

Test for duplicates

curl -H "Origin: https://yourapp.com" -v \ https://api.yourapp.com/endpoint 2>&1 | grep -i "access-control"
# Should return exactly ONE Access-Control-Allow-Origin line
Test your reverse proxy CORS config → CORSFixer