Resolving CORS Issues with Nginx Proxy Configuration: A Step‑by‑Step Guide

This article explains how to diagnose and fix common CORS problems between a localhost frontend and backend by configuring Nginx as a reverse proxy, covering preflight requests, required response headers, multiple error scenarios, and complete Nginx configuration examples.

Laravel Tech Community
Laravel Tech Community
Laravel Tech Community
Resolving CORS Issues with Nginx Proxy Configuration: A Step‑by‑Step Guide

When a frontend running at http://localhost:8080 tries to call a backend API at http://localhost:59200, browsers may block the request due to Cross‑Origin Resource Sharing (CORS) restrictions. The article walks through a systematic analysis and resolution using Nginx as a proxy on port 22222.

The four essential CORS response headers are:

Access‑Control‑Allow‑Origin – specifies permitted origin(s).

Access‑Control‑Allow‑Headers – lists allowed custom request headers.

Access‑Control‑Allow‑Methods – enumerates allowed HTTP methods.

Access‑Control‑Allow‑Credentials – indicates whether cookies may be sent.

A preflight (OPTIONS) request is automatically sent by the browser to verify these headers before the actual request is made.

Initial Nginx proxy configuration (port 22222):

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

After confirming the backend works with Postman, the first error (Case 1) shows a missing Access‑Control‑Allow-Origin header. Adding the header resolves the issue:

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

Because the header is only added for successful responses, the always flag is later required:

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

Case 2: The preflight request returns a non‑OK status. Adding an explicit return 204 for OPTIONS fixes it:

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;
    }
}

Case 3: The request includes an Authorization header, but Access‑Control‑Allow-Headers does not list it. The header is added inside the OPTIONS block:

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;
    }
}

The article notes that Nginx inherits add_header directives only when none are defined at the current level, which explains why headers inside the if block can override previous settings.

Case 4: A PUT request fails because Access‑Control‑Allow-Methods does not include PUT. Adding the method (or using *) resolves the error:

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;
    }
}

Case 5: When the backend also sets CORS headers, Nginx may end up sending duplicate Access‑Control-Allow-Origin values (e.g., * and http://localhost:8080), which browsers reject. The recommendation is to let either the backend or Nginx handle CORS, not both.

Finally, two complete Nginx configurations are provided—one that adds all headers inside the OPTIONS block and another that adds persistent headers for non‑OPTIONS requests—so readers can choose the style that best fits their environment.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

BackendProxyCORS
Laravel Tech Community
Written by

Laravel Tech Community

Specializing in Laravel development, we continuously publish fresh content and grow alongside the elegant, stable Laravel framework.

0 followers
Reader feedback

How this landed with the community

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.