Skip to content

realityone/ngx-cors-rs

Repository files navigation

ngx-cors-rs

A high-performance CORS (Cross-Origin Resource Sharing) module for Nginx written in Rust.

Overview

This project provides a dynamic Nginx module that handles CORS headers with advanced origin matching, preflight request handling, and upstream header merging capabilities. Built using the ngx-rust framework, it offers a modern, safe, and efficient alternative to traditional CORS solutions.

Features

  • Flexible Origin Matching: Support for exact domains, wildcard subdomains, and full wildcard patterns
  • Preflight Request Handling: Automatic handling of OPTIONS requests with proper CORS headers
  • Upstream Header Merging: Merge CORS headers from upstream servers
  • Private Network Access: Support for Private Network Access specification
  • Credential Support: Configurable credential handling for secure cross-origin requests
  • Header Control: Fine-grained control over allowed and exposed headers

Architecture

The project consists of two crates:

  • cors/: Core CORS protocol library with pure Rust implementation
  • ngx_cors_module/: Nginx module wrapper that integrates the CORS library with Nginx

Building

# Debug build (for development)
cargo build

# Release build (optimized for production)
cargo build --release

The module is built as a shared library:

  • Linux: target/debug/libngx_cors_module.so or target/release/libngx_cors_module.so
  • macOS: target/debug/libngx_cors_module.dylib or target/release/libngx_cors_module.dylib

Installation

  1. Build the module (see above)
  2. Add the load directive to your nginx.conf:
load_module /path/to/libngx_cors_module.so;

Configuration Directives

cors_enable

  • Syntax: cors_enable on|off;
  • Default: off
  • Context: http, server, location

Enables or disables CORS processing for the current location.

location /api {
    cors_enable on;
}

cors_origins

  • Syntax: cors_origins <origin> ...;
  • Default: none
  • Context: http, server, location

Specifies allowed origins. Supports multiple matching patterns:

  • *: Wildcard - matches all origins
  • example.com or *.example.com: Matches domain and all subdomains (both http and https)
  • api.example.com: Matches exact subdomain only (both http and https)
# Allow specific domains
cors_origins example.com api.google.com;

# Allow all subdomains
cors_origins *.example.com;

# Allow everything (development only!)
cors_origins *;

Note: It is recommended to specify domains without the scheme (e.g., example.com instead of https://example.com). The module will match both http and https automatically. When a specific origin matches, the response will echo that origin back with its original scheme (e.g., Access-Control-Allow-Origin: https://example.com). Only when the wildcard * pattern matches will the response contain Access-Control-Allow-Origin: *.

cors_methods

  • Syntax: cors_methods <method> ...;
  • Default: GET POST PUT PATCH DELETE HEAD OPTIONS
  • Context: http, server, location

Specifies allowed HTTP methods for CORS requests.

cors_methods GET POST PUT DELETE;

cors_headers

  • Syntax: cors_headers <header> ...;
  • Default: none
  • Context: http, server, location

Specifies allowed request headers that clients can send. These appear in the Access-Control-Allow-Headers response header.

cors_headers Content-Type Authorization X-Custom-Header;

cors_exposed_headers

  • Syntax: cors_exposed_headers <header> ...;
  • Default: none
  • Context: http, server, location

Specifies response headers that browsers are allowed to access. These appear in the Access-Control-Expose-Headers response header.

cors_exposed_headers X-Total-Count X-Page-Number;

cors_max_age

  • Syntax: cors_max_age <seconds>;
  • Default: none
  • Context: http, server, location

Specifies how long (in seconds) preflight request results can be cached by the browser.

cors_max_age 3600;  # Cache for 1 hour

cors_credentials

  • Syntax: cors_credentials on|off;
  • Default: none
  • Context: http, server, location

When enabled, adds Access-Control-Allow-Credentials: true to responses, allowing browsers to send credentials (cookies, authorization headers) with cross-origin requests.

cors_credentials on;

Important: When credentials are enabled, you cannot use the wildcard * for origins. You must specify exact origins.

cors_allow_origin_absent

  • Syntax: cors_allow_origin_absent on|off;
  • Default: off
  • Context: http, server, location

Allows requests that don't include an Origin header to proceed with CORS processing.

cors_allow_origin_absent on;

cors_upstream_merge

  • Syntax: cors_upstream_merge on|off;
  • Default: off
  • Context: http, server, location

When enabled, merges CORS headers from upstream responses with the module's configured headers. Useful when proxying to backends that also set CORS headers.

location /api {
    cors_enable on;
    cors_upstream_merge on;
    proxy_pass http://backend;
}

cors_private_network

  • Syntax: cors_private_network on|off;
  • Default: none
  • Context: http, server, location

Enables Private Network Access support. When a preflight request includes Access-Control-Request-Private-Network: true, the response will include Access-Control-Allow-Private-Network: true.

cors_private_network on;

Request Processing

The module processes requests in two phases:

1. Access Phase (Preflight & Regular Requests)

When a request arrives with an Origin header:

  1. Origin Validation: The module checks if the origin matches any configured pattern in cors_origins

    • Uses specificity-based matching: exact domain > subdomain wildcard > full wildcard
    • Returns the most specific matching pattern
  2. Preflight Detection: If the request is an OPTIONS request with Access-Control-Request-Method header, it's treated as a preflight request

  3. Header Generation:

    • Preflight requests: Generate headers for Access-Control-Allow-Methods, Access-Control-Allow-Headers, Access-Control-Max-Age, etc.
    • Regular requests: Generate headers for Access-Control-Allow-Origin, Access-Control-Expose-Headers, etc.
  4. Response: Headers are added to the response and the request continues through the nginx processing chain

2. Header Filter Phase (Upstream Merge)

When cors_upstream_merge is enabled and the upstream server returns CORS headers:

  1. Header Collection: Collect CORS headers from the upstream response
  2. Merging: Merge upstream headers with module-configured headers
  3. Deduplication: Remove duplicate values while preserving order

Example Configurations

Basic CORS Setup

http {
    server {
        listen 80;
        server_name api.example.com;

        location /api {
            cors_enable on;
            cors_origins app.example.com;
            cors_methods GET POST PUT DELETE;
            cors_headers Content-Type Authorization;
            cors_exposed_headers X-Total-Count;
            cors_max_age 3600;

            # Your backend configuration
            proxy_pass http://backend;
        }
    }
}

Development Setup (Allow All)

location /api {
    cors_enable on;
    cors_origins *;
    cors_methods GET POST PUT PATCH DELETE HEAD OPTIONS;
    cors_headers *;
    cors_credentials off;  # Can't use credentials with wildcard

    proxy_pass http://localhost:3000;
}

Multi-Domain Setup

location /api {
    cors_enable on;
    cors_origins
        app1.example.com
        app2.example.com
        *.partner.com;
    cors_methods GET POST;
    cors_credentials on;
    cors_max_age 7200;

    proxy_pass http://backend;
}

Upstream Merge Setup

location /api {
    cors_enable on;
    cors_origins app.example.com;
    cors_upstream_merge on;  # Merge with backend CORS headers
    cors_headers X-Custom-Header;  # Add to backend's allowed headers

    proxy_pass http://backend;
}

Testing

Unit Tests

# Run all Rust unit tests
cargo test

# Run with output
cargo test -- --nocapture

# Run specific crate tests
cargo test -p cors

Integration Tests

# Run all integration tests
cd tests/cors_module && ./run_tests.sh

# Run specific test
./run_tests.sh cors
./run_tests.sh simple_test

# Run with release build
./run_tests.sh --release

How It Works

Origin Matching Algorithm

The module uses a three-tier specificity system for origin matching:

  1. Exact Domain (Highest priority): api.example.com matches only https://api.example.com or http://api.example.com
  2. Domain with Subdomains (Medium priority): example.com or *.example.com matches example.com, api.example.com, foo.bar.example.com, etc.
  3. Wildcard (Lowest priority): * matches any origin

When multiple patterns match, the most specific one is used.

Preflight Request Flow

Browser                    Nginx (CORS Module)           Backend
   |                              |                         |
   |-- OPTIONS /api ------------->|                         |
   |   Origin: https://app.com    |                         |
   |   Access-Control-Request-    |                         |
   |     Method: POST             |                         |
   |                              |                         |
   |                    [Validate origin]                   |
   |                    [Check if preflight]                |
   |                    [Generate CORS headers]             |
   |                              |                         |
   |<-- 204 No Content ------------|                         |
   |    Access-Control-Allow-     |                         |
   |      Origin: https://app.com |                         |
   |    Access-Control-Allow-     |                         |
   |      Methods: GET, POST      |                         |
   |    Access-Control-Max-Age:   |                         |
   |      3600                    |                         |

Regular Request Flow

Browser                    Nginx (CORS Module)           Backend
   |                              |                         |
   |-- GET /api/data ------------>|                         |
   |   Origin: https://app.com    |                         |
   |                              |                         |
   |                    [Validate origin]                   |
   |                    [Add CORS headers]                  |
   |                              |                         |
   |                              |-- GET /data ----------->|
   |                              |                         |
   |                              |<-- 200 OK --------------|
   |                              |    {data}               |
   |                              |                         |
   |                   [Merge upstream headers if enabled]  |
   |                              |                         |
   |<-- 200 OK -------------------|                         |
   |    Access-Control-Allow-     |                         |
   |      Origin: https://app.com |                         |
   |    Access-Control-Expose-    |                         |
   |      Headers: X-Total-Count  |                         |
   |    {data}                    |                         |

Development

See CLAUDE.md for detailed development instructions, architecture notes, and testing guidelines.

About

A Full-Featured CORS Module for Nginx, Designed for Complex and Messy Applications.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors