Backend Development 15 min read

Mastering CORS: Step-by-Step Nginx Proxy Solutions for Common Errors

This guide walks through diagnosing and fixing typical CORS problems by configuring Nginx as a proxy, explaining the four essential response headers, handling preflight requests, and providing complete server blocks for various error scenarios.

Efficient Ops
Efficient Ops
Efficient Ops
Mastering CORS: Step-by-Step Nginx Proxy Solutions for Common Errors

When you encounter CORS issues, don't immediately copy random solutions; read this comprehensive guide first.

Preparation:

Frontend address: http://localhost:8080

Backend address: http://localhost:59200

Ensure the backend does not handle CORS and verify the API works with Postman.

CORS mainly involves four response headers:

Access-Control-Allow-Origin – specifies allowed request origins.

Access-Control-Allow-Headers – lists permitted custom request headers (checked during preflight).

Access-Control-Allow-Methods – enumerates allowed HTTP methods (checked during preflight).

Access-Control-Allow-Credentials – indicates whether cookies can be sent; set to true only when necessary.

Many articles suggest simply adding these headers in Nginx, but configurations can still fail.

Preflight requests are OPTIONS requests sent by browsers to verify allowed origins, methods, and headers before the actual request.

Example Nginx proxy configuration (port 22222):

<code>server {
    listen       22222;
    server_name  localhost;
    location / {
        proxy_pass http://localhost:59200;
    }
}</code>

Testing the proxy shows the API is reachable.

When the frontend (8080) accesses the proxied API, several error cases appear.

Case 1

<code>Access to XMLHttpRequest at 'http://localhost:22222/api/Login/TestGet' from origin 'http://localhost:8080' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.</code>

The error indicates the preflight response lacks

Access-Control-Allow-Origin

. Add the header in Nginx:

<code>server {
    listen       22222;
    server_name  localhost;
    location / {
        add_header Access-Control-Allow-Origin 'http://localhost:8080';
        proxy_pass http://localhost:59200;
    }
}</code>

After adding, the same error persists because the header is not always returned.

Case 2

<code>Access to XMLHttpRequest at 'http://localhost:22222/api/Login/TestGet' from origin 'http://localhost:8080' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status.</code>

The OPTIONS request receives no 2xx status. Return a 204 for OPTIONS:

<code>server {
    listen       22222;
    server_name  localhost;
    location / {
        add_header Access-Control-Allow-Origin 'http://localhost:8080' always;
        if ($request_method = 'OPTIONS') {
            return 204;
        }
        proxy_pass http://localhost:59200;
    }
}</code>

Case 3

<code>Access to XMLHttpRequest at 'http://localhost:22222/api/Login/TestGet' from origin 'http://localhost:8080' has been blocked by CORS policy: Request header field authorization is not allowed by Access-Control-Allow-Headers in preflight response.</code>

Add the missing header to the preflight response:

<code>server {
    listen       22222;
    server_name  localhost;
    location / {
        add_header Access-Control-Allow-Origin 'http://localhost:8080' always;
        if ($request_method = 'OPTIONS') {
            add_header Access-Control-Allow-Headers 'authorization';
            return 204;
        }
        proxy_pass http://localhost:59200;
    }
}</code>

After fixing, the error reverts to Case 1, showing that multiple

add_header

directives can interfere. Nginx inherits headers only when none are defined at the current level.

There could be several add_header directives. These directives are inherited from the previous level if and only if there are no add_header directives defined on the current level.

To avoid inheritance issues, separate handling for OPTIONS and non‑OPTIONS requests:

<code>server {
    listen       22222;
    server_name  localhost;
    location / {
        if ($request_method = 'OPTIONS') {
            add_header Access-Control-Allow-Origin 'http://localhost:8080';
            add_header Access-Control-Allow-Headers 'authorization';
            return 204;
        }
        if ($request_method != 'OPTIONS') {
            add_header Access-Control-Allow-Origin 'http://localhost:8080' always;
        }
        proxy_pass http://localhost:59200;
    }
}</code>

Case 4

When using a method other than GET/POST (e.g., PUT), the preflight response must include it in

Access-Control-Allow-Methods

:

<code>Access to XMLHttpRequest at 'http://localhost:22222/api/Login/TestGet' from origin 'http://localhost:8080' has been blocked by CORS policy: Method PUT is not allowed by Access-Control-Allow-Methods in preflight response.</code>

Add the method:

<code>server {
    listen 22222;
    server_name localhost;
    location / {
        if ($request_method = 'OPTIONS') {
            add_header Access-Control-Allow-Origin 'http://localhost:8080';
            add_header Access-Control-Allow-Headers 'content-type,authorization';
            add_header Access-Control-Allow-Methods 'PUT';
            return 204;
        }
        if ($request_method != 'OPTIONS') {
            add_header Access-Control-Allow-Origin 'http://localhost:8080' always;
        }
        proxy_pass http://localhost:59200;
    }
}</code>

Setting

Access-Control-Allow-Headers

and

Access-Control-Allow-Methods

to

*

works, but

Access-Control-Allow-Origin

should be specific for security.

Case 5

If the backend already handles CORS, duplicate

Access-Control-Allow-Origin

headers can appear, causing the error:

<code>Access-Control-Allow-Origin header contains multiple values '*', http://localhost:8080, but only one is allowed.</code>

In such situations, choose either backend CORS handling or Nginx proxy handling, not both.

Complete Nginx configuration example (replace

*

with your needs):

<code>server {
    listen 22222;
    server_name localhost;
    location / {
        if ($request_method = 'OPTIONS') {
            add_header Access-Control-Allow-Origin 'http://localhost:8080';
            add_header Access-Control-Allow-Headers '*';
            add_header Access-Control-Allow-Methods '*';
            add_header Access-Control-Allow-Credentials 'true';
            return 204;
        }
        if ($request_method != 'OPTIONS') {
            add_header Access-Control-Allow-Origin 'http://localhost:8080' always;
            add_header Access-Control-Allow-Credentials 'true';
        }
        proxy_pass http://localhost:59200;
    }
}</code>

Following these steps should resolve most CORS problems encountered during development.

backendproxyhttpWeb DevelopmentCORSCross-OriginNginx
Efficient Ops
Written by

Efficient Ops

This public account is maintained by Xiaotianguo and friends, regularly publishing widely-read original technical articles. We focus on operations transformation and accompany you throughout your operations career, growing together happily.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.