How to Configure CORS for 404 Error Responses in Nginx


1 views

When implementing CORS in Nginx for static file serving, developers often encounter a specific challenge: 404 error responses don't inherit the CORS headers configured for successful responses. This becomes problematic when:

location / {
    add_header Access-Control-Allow-Origin *;
    try_files $uri =404;
}

The browser blocks 404 responses because they lack the required CORS headers, even though successful responses work perfectly.

Nginx's add_header directive has a particular behavior worth noting:

  • Headers are only added to successful responses (2xx and 3xx) by default
  • Error responses (4xx, 5xx) won't include these headers
  • This is a security-conscious default behavior

To ensure CORS headers appear on 404 responses, we need to explicitly handle error cases. Here's the complete solution:

server {
    # ... other server config ...

    location / {
        # Always add CORS headers
        add_header Access-Control-Allow-Origin *;
        
        # Handle CORS preflight requests
        if ($request_method = 'OPTIONS') {
            add_header Access-Control-Allow-Origin *;
            add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
            add_header Access-Control-Allow-Headers 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
            add_header Access-Control-Max-Age 1728000;
            add_header Content-Type 'text/plain; charset=utf-8';
            add_header Content-Length 0;
            return 204;
        }
        
        # Serve files with CORS headers
        try_files $uri =404;
        
        # Ensure headers are added to error responses
        error_page 404 = @cors_404;
    }
    
    location @cors_404 {
        add_header Access-Control-Allow-Origin *;
        return 404;
    }
}

For a more modular solution that works across all error codes, consider using the headers_more module:

load_module modules/ngx_http_headers_more_filter_module.so;

http {
    more_set_headers 'Access-Control-Allow-Origin: *';
}

This approach adds the header to all responses, including errors. Note that you'll need to install the headers_more module separately.

Verify your setup works with these commands:

# Check for successful response
curl -I http://yourserver.com/existing-file

# Check for 404 response
curl -I http://yourserver.com/non-existent-file

Both responses should include the Access-Control-Allow-Origin: * header.


When serving static files via Nginx with CORS enabled, you might notice that while successful responses (200 OK) include the necessary Access-Control-Allow-Origin header, 404 error responses don't. This becomes problematic when client-side JavaScript needs to handle missing resources gracefully.

By default, Nginx only applies CORS headers to successful responses. The 404 error pages are generated by Nginx's error handling system, which bypasses the location block where you typically define CORS headers.

We need to modify Nginx's error handling to include CORS headers. Here's how to do it:


server {
    listen 80;
    server_name example.com;

    # Standard CORS configuration for successful responses
    location / {
        add_header 'Access-Control-Allow-Origin' '*';
        add_header 'Access-Control-Allow-Methods' 'GET, OPTIONS';
        add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
        add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
        
        try_files $uri $uri/ =404;
    }

    # Special handling for 404 errors
    error_page 404 /404.html;
    location = /404.html {
        internal;
        add_header 'Access-Control-Allow-Origin' '*';
        add_header 'Access-Control-Allow-Methods' 'GET, OPTIONS';
        add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
        root /path/to/your/error/files;
    }
}

For Nginx versions 1.7.5 and above, you can use the always parameter to ensure headers are added to all responses:


location / {
    add_header 'Access-Control-Allow-Origin' '*' always;
    add_header 'Access-Control-Allow-Methods' 'GET, OPTIONS' always;
    # ... other CORS headers with 'always'
    
    try_files $uri $uri/ =404;
}

After making these changes, test with:


curl -I http://yourdomain.com/nonexistent-file

You should see the CORS headers in the 404 response.

Adding headers to error responses has minimal performance impact. However, if you're serving many 404s, consider:

  • Implementing proper caching headers for your error pages
  • Using a CDN to handle static files and errors
  • Monitoring 404 rates to identify missing resources

For SPAs that might request various assets:


server {
    # ... other server config
    
    location /assets/ {
        add_header 'Access-Control-Allow-Origin' '*' always;
        try_files $uri =404;
    }
    
    location /api/ {
        # API-specific CORS configuration
    }
    
    location / {
        # SPA fallback route
        try_files $uri /index.html;
    }
}