NGINX CORS Configuration — Adding Cross-Origin Headers at the Reverse Proxy Layer
In this tutorial, you will learn about NGINX CORS Configuration. We cover key concepts, practical examples, and best practices to help you master this topic.
NGINX can add CORS headers at the reverse proxy layer, handling preflight requests and injecting Access-Control-* headers before forwarding to backend servers, enabling centralized CORS management.
What You'll Learn
- Adding CORS headers in NGINX location blocks
- Handling OPTIONS preflight requests
- Dynamic origin configuration with maps
Why It Matters
Reverse proxy CORS configuration centralizes cross-origin policy management without modifying backend code. DodaTech's API Gateway uses NGINX to apply consistent CORS policies across Microservices.
flowchart LR
A["Browser"] --> B["NGINX Proxy"]
B --> C{"Request method?"}
C -->|"OPTIONS"| D["NGINX responds with CORS headers"]
C -->|"Other"| E["Forward to backend"]
E --> F["Backend responds"]
F --> G["NGINX adds CORS headers"]
G --> H["Response to browser"]
style D fill:#fef08a,stroke:#ca8a04
style B fill:#dbeafe,stroke:#2563eb
Code Examples
# Basic CORS configuration in NGINX
server {
listen 443 ssl;
server_name api.example.com;
location /api/ {
# Handle preflight
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' 'Content-Type, Authorization';
add_header 'Access-Control-Max-Age' 86400;
add_header 'Content-Type' 'text/plain charset=UTF-8';
add_header 'Content-Length' 0;
return 204;
}
# Add CORS headers to actual responses
add_header 'Access-Control-Allow-Origin' 'https://app.example.com' always;
add_header 'Access-Control-Allow-Credentials' 'true' always;
add_header 'Access-Control-Expose-Headers' 'X-RateLimit-Remaining' always;
proxy_pass http://backend:3000;
}
}
# Dynamic CORS origins with map
map $http_origin $cors_origin {
default "";
"~^https://app\.example\.com$" "$http_origin";
"~^https://[a-z]+\.admin\.example\.com$" "$http_origin";
"~^https://dashboard\.example\.com$" "$http_origin";
}
map $request_method $cors_methods {
default "GET, POST, PUT, DELETE, OPTIONS";
OPTIONS "GET, POST, PUT, DELETE, OPTIONS";
}
server {
location /api/ {
if ($cors_origin = "") {
# Origin not allowed, do not add CORS headers
}
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' $cors_origin;
add_header 'Access-Control-Allow-Methods' $cors_methods;
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization';
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Max-Age' 86400;
add_header 'Content-Type' 'text/plain charset=UTF-8';
add_header 'Content-Length' 0;
return 204;
}
add_header 'Access-Control-Allow-Origin' $cors_origin always;
add_header 'Access-Control-Allow-Credentials' 'true' always;
proxy_pass http://backend:3000;
}
}
# CORS for multiple subdomains with regex
map $http_origin $allow_origin {
default "false";
"~^https://(www\.)?example\.com$" "true";
"~^https://[a-z0-9-]+\.example\.com$" "true";
}
server {
location /api/ {
if ($allow_origin = "true") {
add_header 'Access-Control-Allow-Origin' $http_origin always;
}
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' $http_origin;
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT, User-Agent, X-Requested-With, Content-Type, Authorization';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain; charset=utf-8';
add_header 'Content-Length' 0;
return 204;
}
proxy_pass http://backend:3000;
}
}
# Test NGINX CORS headers
curl -I -H "Origin: https://app.example.com" \
https://api.example.com/api/data
# Test preflight
curl -X OPTIONS -I \
-H "Origin: https://app.example.com" \
-H "Access-Control-Request-Method: POST" \
https://api.example.com/api/data
Common Mistakes
1. Using if Blocks Incorrectly
NGINX if statements can cause unexpected behavior. Use maps for origin validation instead.
2. Not Using the Always Parameter
Without always, add_header is only applied for 2xx and 3xx responses. Use always for all responses.
3. Forgetting to Handle OPTIONS for Preflight
Without OPTIONS handling, preflight requests are forwarded to the backend, which may block them.
4. Conflicting Headers with Backend
If the backend also sets CORS headers, they can duplicate or conflict. Remove backend CORS when using NGINX.
5. Not Validating Dynamic Origins
Using $http_origin directly without validation allows any origin. Always validate with a map.
Practice Questions
- How do you add CORS headers in NGINX?
- How does NGINX handle OPTIONS preflight requests?
- What is the map directive used for in CORS configuration?
- Why use the always parameter with add_header?
- How do you handle multiple allowed origins?
Answers:
- Use the add_header directive with Access-Control-* headers.
- Use an if ($request_method = 'OPTIONS') block to return appropriate headers.
- The map directive evaluates the Origin header against patterns and returns the allowed origin or empty string.
- The always parameter ensures headers are added to all responses, including errors.
- Use the map directive with regex patterns to validate and return the matching origin.
Challenge: Set up NGINX as a reverse proxy for a Microservices Architecture with three backend services, each with different CORS requirements. Implement a unified CORS configuration with dynamic origins, proper preflight handling, and centralized logging of CORS rejections.
FAQ
Mini Project
Build an NGINX-based API gateway with dynamic CORS support for multiple backend services. Implement origin validation with maps, preflight handling, credential support, and CORS header stripping from backend responses. Add a monitoring endpoint that reports CORS rejection statistics.
What's Next
Learn Apache CORS configuration using mod_headers, then explore CORS error handling patterns for debugging.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro