解决NGINX中'Access-Control-Allow-Origin'多值重复CORS错误
I’ve dealt with this exact duplicate CORS header issue many times with WordPress + Nginx setups, so let’s walk through fixing it step by step. The core problem is that both Nginx and WordPress (or a plugin/theme) are adding the same CORS headers, leading to the duplicate values the browser is complaining about.
1. First, Diagnose Where the Duplicates Are Coming From
Run this curl command to inspect the response headers for your API endpoint. This will tell you if WordPress is adding headers even when Nginx isn’t:
curl -H "Origin: https://test.example.com" -I https://api.example.com/wp-json/custom-post/v1/some-data/
If you still see duplicate Access-Control-Allow-Origin or Access-Control-Allow-Credentials headers after temporarily removing Nginx’s CORS add_header lines, that confirms WordPress (a plugin, theme, or custom code) is adding the headers too.
2. Fix Your Nginx CORS Configuration
Your current config adds CORS headers globally, which can clash with WordPress’s headers. Replace your existing CORS block with this more targeted, conflict-resistant setup:
# Handle preflight OPTIONS requests (required for AJAX with custom headers) if ($request_method = OPTIONS) { if ($http_origin ~ '^https?:\/\/(localhost|test\.example\.com)$') { add_header Access-Control-Allow-Origin $http_origin; add_header Access-Control-Allow-Credentials true; add_header Access-Control-Allow-Headers $http_access_control_request_headers; add_header Access-Control-Allow-Methods "GET, POST, OPTIONS, PUT, DELETE"; add_header Content-Length 0; add_header Content-Type text/plain; return 204; # No-content response for preflight checks } } # Add CORS headers to non-OPTIONS requests (only for allowed origins) if ($http_origin ~ '^https?:\/\/(localhost|test\.example\.com)$') { # Use "always" to ensure headers are sent even for error responses (e.g., 404s) add_header Access-Control-Allow-Origin $http_origin always; add_header Access-Control-Allow-Credentials true always; }
This setup:
- Only responds to preflight OPTIONS requests from your trusted origin
- Uses the
alwaysflag to guarantee headers are sent for non-success responses (like 404s for missing media) - Avoids adding empty headers when the origin isn’t allowed
3. Track Down WordPress’s Hidden CORS Headers
If curl still shows duplicates after updating Nginx, hunt for where WordPress is adding the headers:
- Theme
functions.php: Look for lines likeheader('Access-Control-Allow-Origin: https://test.example.com')oradd_action('rest_api_init', ...)that modifies REST API headers. - Plugins: Temporarily disable all plugins and test. If the duplicates disappear, re-enable them one by one to find the culprit—many security, SEO, or API-focused plugins add CORS headers automatically.
- Log WordPress headers: Add this snippet to your
wp-config.phpto log all headers WordPress sends (check your Nginx error log after making a request):add_action('send_headers', function() { error_log('WP Response Headers: ' . print_r(headers_list(), true)); }); - PHP auto-prepend files: Check your
php.inifor theauto_prepend_filedirective—this could load a hidden script that adds CORS headers.
4. Ensure Media Files Get Proper CORS Headers
When you removed Nginx’s CORS config earlier, media files failed because they’re served directly by Nginx (not WordPress). The updated Nginx config above fixes this, as it adds CORS headers for all requests (including static media) from your trusted origin.
Final Steps
- Run
nginx -tto validate your updated config. - Reload Nginx with
sudo systemctl reload nginx. - Clear your browser cache and test again—the duplicate headers should be gone, and CORB errors will resolve automatically once your CORS headers are valid.
内容的提问来源于stack exchange,提问作者Davy

