Fix CORS Error in Nginx — Reverse Proxy Configuration
Nginx acts as a reverse proxy in front of your app. Unless you explicitly add CORS headers at the Nginx level, they may be stripped or never sent. This config handles both preflight and regular requests.
Browser Console Error
Access to XMLHttpRequest at 'https://api.example.com' from origin 'https://app.example.com' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present.Add to your server block
server {
listen 443 ssl http2;
server_name api.example.com;
location / {
# Handle OPTIONS preflight — return immediately without proxying
if ($request_method = OPTIONS) {
add_header Access-Control-Allow-Origin "https://app.example.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-Allow-Credentials "true";
add_header Access-Control-Max-Age 86400;
add_header Content-Length 0;
add_header Content-Type text/plain;
return 204;
}
# Add CORS headers to all other responses
add_header Access-Control-Allow-Origin "https://app.example.com" always;
add_header Access-Control-Allow-Credentials "true" always;
proxy_pass http://localhost:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
The always flag ensures headers appear on error responses too. Without it, a 401 or 500 response will not have CORS headers — the browser will show a CORS error instead of your actual error message, making debugging confusing.
Multiple allowed origins
map $http_origin $cors_origin {
default "";
"https://app.example.com" "https://app.example.com";
"https://staging.example.com" "https://staging.example.com";
}
server {
location / {
if ($request_method = OPTIONS) {
add_header Access-Control-Allow-Origin $cors_origin;
add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS";
add_header Access-Control-Allow-Headers "Authorization, Content-Type";
add_header Content-Length 0;
return 204;
}
add_header Access-Control-Allow-Origin $cors_origin always;
add_header Vary Origin always;
proxy_pass http://localhost:3000;
}
}
Test and reload
nginx -t # test configuration — fix errors before reloading nginx -s reload # apply changes without dropping connections
After reloading, use CORSFixer to send a real preflight to your API and verify the response headers are correct.
Send a live preflight to your Nginx API →