5

I am currently trying to only return a set of CORS headers conditionally using Nginx. At first it seemed like a simple task, as I already had this working config:

upstream api-app {
  server unix:/tmp/api-app.sock fail_timeout=0;
}

server { listen 80;

[other server stuff...]

CORS headers added to all ALL responses of this server (needed for all requests)

more_set_headers 'Access-Control-Allow-Methods: GET,POST,PATCH,PUT,DELETE,OPTIONS'; more_set_headers 'Access-Control-Allow-Origin: *'; more_set_headers 'Access-Control-Allow-Credentials: true'; more_set_headers 'Access-Control-Request-Method: GET,POST,PATCH,PUT,DELETE,OPTIONS'; more_set_headers 'Access-Control-Request-Headers: Content-Type'; more_set_headers 'Access-Control-Allow-Headers: Origin,X-Requested-With,Content-Type,Accept,Session-Id,Role-Id,Visitor-Id,X-Window-Location'; more_set_headers 'Access-Control-Expose-Headers: X-Total-Entries,X-Total-Pages,X-Page,X-Per-Page,X-Folder-Hierarchy';

location / { # For OPTIONS request only return above headers and no content (also allow caching them) if ($request_method = 'OPTIONS') { # cache above (Access-Control) headers add_header 'Access-Control-Max-Age' 600;

  # set content length and type just for good measure:
  add_header 'Content-Length' 0;
  add_header 'Content-Type' 'text/plain charset=UTF-8';

  # return 204 - no content
  return 204;
}

# Forward requests to actual app (if all above conditions did not match)
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_read_timeout 35;
proxy_send_timeout 35;

proxy_pass http://api-app;

} }

So I just wanted to change the CORS header section into s.th. like this

# ...
  set $cors '';

if ($http_origin ~ '^https?://(some.domain|some.other.domain|other-allows.domain)') { set $cors 'true'; }

if ($cors = 'true') { more_set_headers 'Access-Control-Allow-Methods: GET,POST,PATCH,PUT,DELETE,OPTIONS'; more_set_headers 'Access-Control-Allow-Origin: $http_origin'; more_set_headers 'Access-Control-Allow-Credentials: true'; more_set_headers 'Access-Control-Request-Method: GET,POST,PATCH,PUT,DELETE,OPTIONS'; more_set_headers 'Access-Control-Request-Headers: Content-Type'; more_set_headers 'Access-Control-Allow-Headers: Origin,X-Requested-With,Content-Type,Accept,Session-Id,Role-Id,Visitor-Id,X-Window-Location'; more_set_headers 'Access-Control-Expose-Headers: X-Total-Entries,X-Total-Pages,X-Page,X-Per-Page,X-Folder-Hierarchy'; }

location / {

...

However nginx -t quickly told me more_set_headers can't be used in an if statement. Neither can add_header.

I figured out that in the location section it would work. But I already knew this won't be good solution as we all know if is evil in nginx location sections. And I was right as then CORS headers would be lost for the OPTIONS request, I already had an if case for (an OK one, as it returns).

I also came accross the option to use Nginxs map function. But that only works if I want to alter header values, not if I want to add an entire block of headers.

So my question is the following: Is there a way to add headers conditionally (not using map) and outside the location block? Ideally allowing for an include of the CORS section so I can use it in multiple servers (i.e. nginx sites).

1 Answers1

4

I don't know if more_set_headers accepts empty strings. If it accepts, then you can define multiple map statements:

map $http_origin $cors_methods {
    default "";
    ~^https?://(some.domain|some.other.domain|other-allows.domain) Access-Control-Allow-Methods: GET,POST,PATCH,PUT,DELETE,OPTIONS;
}

map $http_origin $cors_origin { default ""; ~^https?://(some.domain|some.other.domain|other-allows.domain) Access-Control-Allow-Origin: $http_origin; }

And then use this:

more_set_headers $cors_methods;
more_set_headers $cors_origin;
Tero Kilkanen
  • 38,887