Configuring CORS in Nginx allows a browser application served from one origin to call an API on another origin without being blocked by the browser’s cross-site protections. Done correctly, cross-origin access becomes explicit and predictable instead of relying on trial-and-error header tweaks.

Browsers include an Origin request header and decide whether JavaScript can read the response based on CORS response headers like Access-Control-Allow-Origin. Requests that use non-simple methods, non-simple Content-Type values (such as application/json), or custom headers (such as Authorization) usually trigger a preflight OPTIONS request that must return the expected allow headers before the browser proceeds.

Overly broad CORS rules can expose authenticated responses to untrusted sites when cookies or other browser credentials are involved, even though server-side authentication still exists. Prefer a specific origin, keep rules scoped to only the location blocks that need cross-origin access, and use the always modifier so headers are present on error responses as well.

Steps to configure CORS headers in Nginx:

  1. Decide the exact allowed origin and whether the browser must send credentials.

    The origin must match scheme, host, and port, such as https://app.example.com or http://localhost:3000.

  2. Add CORS headers and a preflight OPTIONS response in the relevant API location block.
    location /api/ {
        add_header Access-Control-Allow-Origin "https://app.example.com" always;
        add_header Access-Control-Allow-Methods "GET, POST, OPTIONS" always;
        add_header Access-Control-Allow-Headers "Authorization, Content-Type" always;
        add_header Access-Control-Max-Age "86400" always;
    
        if ($request_method = OPTIONS) {
            return 204;
        }
    
        ##### snipped #####
    }

    Do not combine Access-Control-Allow-Credentials: true with Access-Control-Allow-Origin: *; browsers reject that combination, and overly permissive CORS can leak authenticated responses to malicious origins.

  3. Test the Nginx configuration for syntax errors.
    $ sudo nginx -t
    nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
    nginx: configuration file /etc/nginx/nginx.conf test is successful
  4. Reload Nginx to apply the changes.
    $ sudo systemctl reload nginx
  5. Verify the preflight response returns CORS headers for an OPTIONS request.
    $ curl -i -X OPTIONS \
      -H 'Origin: https://app.example.com' \
      -H 'Access-Control-Request-Method: POST' \
      -H 'Access-Control-Request-Headers: Authorization, Content-Type' \
      http://127.0.0.1/api/
    HTTP/1.1 204 No Content
    Server: nginx
    Date: Mon, 13 May 2024 10:15:01 GMT
    Connection: keep-alive
    Access-Control-Allow-Origin: https://app.example.com
    Access-Control-Allow-Methods: GET, POST, OPTIONS
    Access-Control-Allow-Headers: Authorization, Content-Type
    Access-Control-Max-Age: 86400

    If the browser requests additional headers in Access-Control-Request-Headers, add them to Access-Control-Allow-Headers or the preflight will fail.

  6. Verify a normal request includes Access-Control-Allow-Origin when an Origin header is present.
    $ curl -i -H 'Origin: https://app.example.com' http://127.0.0.1/api/
    HTTP/1.1 200 OK
    Server: nginx
    Date: Mon, 13 May 2024 10:15:07 GMT
    Content-Type: application/json
    Connection: keep-alive
    Access-Control-Allow-Origin: https://app.example.com
    ##### snipped #####
Discuss the article:

Comment anonymously. Login not required.