CORS in Nginx and Apache: Configurations That Actually Work

Setting CORS at the web server level is often the cleanest approach. Your application doesn’t need to know about CORS at all — Nginx or Apache handles it before the request even reaches your app.

Here are configurations I’ve used in production that work.

Nginx#

Basic: Single Origin#

server {
    listen 80;
    server_name api.example.com;
    
    location / {
        add_header Access-Control-Allow-Origin "https://myapp.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-Allow-Credentials "true";
        add_header Access-Control-Max-Age "86400";
        add_header Access-Control-Expose-Headers "X-Total-Count, X-Request-Id";
        
        # Handle preflight
        if ($request_method = OPTIONS) {
            add_header Access-Control-Allow-Origin "https://myapp.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-Allow-Credentials "true";
            add_header Access-Control-Max-Age "86400";
            return 204;
        }
        
        proxy_pass http://127.0.0.1:3000;
    }
}

Note: You need to repeat the add_header directives inside the if block because Nginx’s if directive creates a new context. Headers set outside the if don’t apply inside it. This is a well-known Nginx gotcha.