Mastering CORS: Real‑World Backend Configurations and Chrome Private‑Network Fixes

This article shares a hands‑on journey of solving cross‑origin issues in a multi‑domain education product, covering CORS fundamentals, simple and preflight requests, Nginx and SpringBoot configurations, response‑code choices, and Chrome’s insecure private‑network restrictions, with practical solutions and lessons learned.

Su San Talks Tech
Su San Talks Tech
Su San Talks Tech
Mastering CORS: Real‑World Backend Configurations and Chrome Private‑Network Fixes

1. Encountering Cross‑Origin

During the incubation of an education product, the author, acting as an architect, faced a cross‑origin scenario where multiple front‑ends (institution, authority, parent portals) each had separate domains accessed via PC, WeChat, or QR‑code H5.

2. CORS Details

CORS (Cross‑Origin Resource Sharing) is a W3C standard that allows browsers to send XMLHttpRequest to a different origin, overcoming the same‑origin limitation.

Same origin means identical protocol, domain, and port.

When a user accesses http://admin.training.com and the API is at http://api.training.com, the request is cross‑origin.

2.1 Simple Request

If a request meets all of the following conditions, the browser sends a simple request; otherwise it performs a preflight request.

Method is GET, POST, or HEAD.

Only safe request headers are used:

Accept

Accept‑Language

Content‑Language

Content‑Type limited to text/plain, multipart/form-data, or application/x‑www‑form‑urlencoded HTML‑related headers such as DPR, Download, Save‑Data, Viewport‑Width, Width

No event listeners are registered on XMLHttpRequestUpload.

No ReadableStream is used in the request.

For a simple request the browser adds an Origin header and the server responds with Access‑Control‑Allow‑Origin and related headers.

Example response header allowing any origin: Access-Control-Allow-Origin: * If the server restricts to http://admin.training.com the header would be:

Access-Control-Allow-Origin: http://admin.training.com

2.2 Preflight Request

For non‑simple requests the browser first sends an OPTIONS preflight request to ask the server whether the actual request is permitted.

The preflight request includes headers such as Access-Control-Request-Method and Access-Control-Request-Headers.

If the server approves, the browser proceeds with the actual request; otherwise it aborts.

3. Backend Configuration

Two stable approaches were tested over two months:

MND‑recommended Nginx configuration.

SpringBoot’s built‑in CorsFilter configuration.

▍MND‑Recommended Nginx Configuration

Configure CORS at the request‑forwarding layer:

location / {
    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;
    }
    if ($request_method = 'POST') {
        add_header 'Access-Control-Allow-Origin' '*' always;
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
        add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range' always;
        add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always;
    }
    if ($request_method = 'GET') {
        add_header 'Access-Control-Allow-Origin' '*' always;
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
        add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range' always;
        add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always;
    }
}

Using a wildcard for Access-Control-Allow-Headers simplifies handling custom headers such as signatures and tokens.

IE11 requires the header list to be comma‑separated; otherwise it reports “Access‑Control‑Allow‑Headers does not contain request header content‑type”.

▍SpringBoot Built‑In CorsFilter

Typical SpringBoot CORS setup:

public void addCorsMappings(CorsRegistry registry) {
    registry.addMapping("/**")
        .allowedOrigins("*")
        .allowedMethods("POST", "GET", "PUT", "OPTIONS", "DELETE")
        .allowCredentials(true)
        .allowedHeaders("*")
        .maxAge(3600);
}

In practice the configuration did not take effect because an ActionInterceptor processed the request before the CORS filter, consuming the OPTIONS request and returning a JSON error.

Switching to a dedicated CorsFilter solved the ordering issue, as filters have the highest precedence.

private CorsConfiguration corsConfig() {
    CorsConfiguration cors = new CorsConfiguration();
    cors.addAllowedOrigin("*");
    cors.addAllowedHeader("*");
    cors.addAllowedMethod("*");
    cors.setAllowCredentials(true);
    cors.setMaxAge(3600L);
    return cors;
}

@Bean
public CorsFilter corsFilter() {
    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    source.registerCorsConfiguration("/**", corsConfig());
    return new CorsFilter(source);
}

When Access-Control-Allow-Headers is a wildcard, the filter echoes back the request’s Access-Control-Request-Headers as a comma‑separated list, satisfying IE11.

4. Preflight Response Code: 200 vs 204

Team members asked whether the preflight response should be 200 or 204. MDN originally showed 200; later it was updated to 204. In practice, 200 works everywhere, and 204 is also well‑supported by modern browsers.

5. Chrome: Insecure Private Network

During an internal demo, a page triggered a CORS error labeled InsecurePrivateNetwork after Chrome 94 introduced a new security feature.

Temporary fix: disable the flag chrome://flags/#block-insecure-private-network-requests and restart Chrome.

Root cause: the page accessed a private‑network IP (e.g., 172.16.x.x) from a public network, which Chrome blocks by default.

Official mitigation steps:

Serve private‑network resources over HTTPS.

Future browsers may require a header such as Access-Control-Request-Private-Network.

Work‑arounds include using another browser, disabling the flag, or mapping the host to an external IP.

6. Review

The API gateway (e.g., Meituan Shepherd, Tencent) proved valuable for unified domain management, authentication, rate‑limiting, and CORS handling, allowing backend services to stay agnostic of cross‑origin concerns.

The author’s mindset shifted from underestimating CORS to methodically understanding its mechanisms, which helped resolve the issues efficiently.

7. Final Thoughts

A story from a technical talk illustrated how a seemingly minor detail (e.g., a CLOSE_WAIT state) can pinpoint the root cause of a problem quickly. The same principle applies to CORS: paying attention to details leads to effective solutions.

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.

BackendCORSCross-OriginNGINXSpringBootChromepreflight
Su San Talks Tech
Written by

Su San Talks Tech

Su San, former staff at several leading tech companies, is a top creator on Juejin and a premium creator on CSDN, and runs the free coding practice site www.susan.net.cn.

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.