How Do I Set Access-Control-Allow-Origin On Upstream Errors In Nginx?

- 1 answer

I know there are a million answers to "how to set Access-Control-Allow-Origin in nginx" but unfortunately if there's an answer to my specific question, it's buried with all the basic answers.

I have an Angular app pointing to an nginx server with a rest service upstream - all running on my local laptop inside a docker compose.

The nginx service is configured fairly simply:

    upstream REST {
       # rest-service is mapped by docker-compose to the correct container
       server rest-service:8080;

    server {
        listen 80;
        listen [::]:80;
        # This is mapped in /etc/hosts on my laptop

        location / {
            proxy_hide_header 'Access-Control-Allow-Origin';
            # web-ui is also mapped in /etc/hosts on my laptop
            add_header "Access-Control-Allow-Origin" "";

            add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS';
            add_header 'Access-Control-Max-Age' 1728000; # 20 days
            add_header 'Access-Control-Allow-Credentials' 'true';
            add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization';

            add_header Reverse-Proxy true;
            proxy_set_header X-Real-IP       $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header Host            $http_host;
            proxy_pass http://REST;

If I call a valid URL on the rest service, e.g. (this is an unauthenticated endpoint), everything works as expected:

curl -v ''
< Access-Control-Allow-Origin:
< Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
< Access-Control-Max-Age: 1728000
< Access-Control-Allow-Credentials: true
< Access-Control-Allow-Headers: Content-Type, Authorization
< Reverse-Proxy: true
* Connection #0 to host left intact
{"status":"OK","name":"foo",...}* Closing connection 0

However, if I call an invalid URL, say (which is an authenticated endpoint, and I provide no credentials), I get back the expected error, but there are no CORS headers. In a browser this causes everything to flake out so I don't even get the error message in Angular to display to the user:

curl -v ''
*   Trying
* Connected to ( port 80 (#0)
> GET /api/v1/accounts HTTP/1.1
> Host:
> User-Agent: curl/7.64.1
> Accept: */*
< HTTP/1.1 403
< Server: nginx/1.21.6
< Date: Wed, 09 Feb 2022 20:57:49 GMT
< Content-Type: application/json
< Transfer-Encoding: chunked
< Connection: keep-alive
< Set-Cookie: JSESSIONID=F3B21A55841C8E5A6F838F2427A0E22B; Path=/; HttpOnly
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 1; mode=block
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: 0
< X-Frame-Options: DENY
* Connection #0 to host left intact
{"timestamp":"2022-02-09T20:57:49.526+00:00","status":403,"error":"Forbidden","message":"Access Denied","path":"/api/v1/accounts"}* Closing connection 0

Browser console:

Access to XMLHttpRequest at '' from origin '' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

So... Is there a way to make sure the CORS headers are still added to the response by nginx even if the response from the upstream server is an error (401/403/404/etc)?



You need to add always to each header you want added to error responses. It was added in 1.7.5: