-
-
Save alexjs/4165271 to your computer and use it in GitHub Desktop.
# | |
# Slightly tighter CORS config for nginx | |
# | |
# A modification of https://gist.github.com/1064640/ to include a white-list of URLs | |
# | |
# Despite the W3C guidance suggesting that a list of origins can be passed as part of | |
# Access-Control-Allow-Origin headers, several browsers (well, at least Firefox) | |
# don't seem to play nicely with this. | |
# | |
# To avoid the use of 'Access-Control-Allow-Origin: *', use a simple-ish whitelisting | |
# method to control access instead. | |
# | |
# NB: This relies on the use of the 'Origin' HTTP Header. | |
location / { | |
if ($http_origin ~* (whitelist\.address\.one|whitelist\.address\.two)) { | |
set $cors "true"; | |
} | |
# Nginx doesn't support nested If statements. This is where things get slightly nasty. | |
# Determine the HTTP request method used | |
if ($request_method = 'OPTIONS') { | |
set $cors "${cors}options"; | |
} | |
if ($request_method = 'GET') { | |
set $cors "${cors}get"; | |
} | |
if ($request_method = 'POST') { | |
set $cors "${cors}post"; | |
} | |
if ($cors = "true") { | |
# Catch all incase there's a request method we're not dealing with properly | |
add_header 'Access-Control-Allow-Origin' "$http_origin"; | |
} | |
if ($cors = "trueget") { | |
add_header 'Access-Control-Allow-Origin' "$http_origin"; | |
add_header 'Access-Control-Allow-Credentials' 'true'; | |
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; | |
add_header 'Access-Control-Allow-Headers' 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type'; | |
} | |
if ($cors = "trueoptions") { | |
add_header 'Access-Control-Allow-Origin' "$http_origin"; | |
# | |
# Om nom nom cookies | |
# | |
add_header 'Access-Control-Allow-Credentials' 'true'; | |
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; | |
# | |
# Custom headers and headers various browsers *should* be OK with but aren't | |
# | |
add_header 'Access-Control-Allow-Headers' 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type'; | |
# | |
# Tell client that this pre-flight info is valid for 20 days | |
# | |
add_header 'Access-Control-Max-Age' 1728000; | |
add_header 'Content-Type' 'text/plain charset=UTF-8'; | |
add_header 'Content-Length' 0; | |
return 204; | |
} | |
if ($cors = "truepost") { | |
add_header 'Access-Control-Allow-Origin' "$http_origin"; | |
add_header 'Access-Control-Allow-Credentials' 'true'; | |
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; | |
add_header 'Access-Control-Allow-Headers' 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type'; | |
} | |
} |
$http_origin is empty on my system. Is there some mechanism needed to have this available?
curl -I -X OPTIONS -H "Origin: http://www.example.com" http://www.yourdomain.com
Hi,
I have observed when I use an if statement within a location then nginx returns a 404.
So the below does not work:
if ($cors = "true") {
add_header 'Access-Control-Allow-Origin' "$http_origin";
}
try_files $uri $uri/ /index.php?$args;
causes nginx to not reach the try_files directive and return a 404.
Anyone else with this issue?
@spyrospph I have the same problem, and it's also documented at IfIsEvil.
imho, if there are no ways to add cors headers without using ifs, don't use nginx.
What does 'X-Mx-ReqToken' header actually do? I see it in all CORS tutorials for nginx but can't find any info otherwise.
This is very helpful
According to nginx, if
shouldn't be used in a location
block like this.
Correct ^
All this if
s are different levels and should not work together according to add_header
usage docs.
Very well, thanks
Thanks for this. I think you want a
$
at the end of the regex on line 18. Without it my sitehttp://whitelist.address.one.evil.com
will pass and show up in theAccess-Control-Allow-Origin
header.
+1 came here to say this
it is great,thank you ,it works.
Because the use of if is evil inside location like @mox601 and @bfricka said before, this was my solution::
location / {
try_files $uri $uri/ /index.php$is_args$args;
}
location ~ \.php$ {
try_files $uri /index.php =404;
fastcgi_pass php-upstream;
fastcgi_index index.php;
fastcgi_buffers 16 16k;
fastcgi_buffer_size 32k;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
#fixes timeouts
fastcgi_read_timeout 600;
include fastcgi_params;
if ($request_method = "OPTIONS") {
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Credentials true;
add_header Access-Control-Allow-Methods 'DELETE,GET,OPTIONS,POST,PUT';
add_header Access-Control-Allow-Headers 'Accept,Authorization,Cache-Control,Content-Type,DNT,If-Modified-Since,Keep-Alive,Origin,User-Agent,X-Requested-With,X-Token-Auth,X-Mx-ReqToken,X-Requested-With';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain charset=UTF-8';
add_header 'Content-Length' 0;
return 204;
}
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Credentials true;
add_header Access-Control-Allow-Methods 'DELETE,GET,OPTIONS,POST,PUT';
add_header Access-Control-Allow-Headers 'Accept,Authorization,Cache-Control,Content-Type,DNT,If-Modified-Since,Keep-Alive,Origin,User-Agent,X-Requested-With,X-Token-Auth,X-Mx-ReqToken,X-Requested-With';
}
I couldn't even get if
to work at all without a return
at the end, which didn't work for me because I needed to do proxy_pass
. So this is the solution I ended up with, using map
:
# If the request is from an allowed domain or subdomain of that domain,
# we set the values of the CORS headers to be ued below
map $http_origin $use_cors {
'~*^https?://(.+\.)*(whitelist\.address\.one|whitelist\.address\.two)' 'true';
}
map $use_cors $allow_origin {
'true' $http_origin;
}
map $use_cors $allow_methods {
'true' 'GET, OPTIONS, POST';
}
map $use_cors $allow_headers {
'true' 'User-Agent,Keep-Alive,Content-Type,Pragma,Cache-Control,Upgrade-Insecure-Requests';
}
server {
...
location / {
# This prevents OPTIONS requests from failing
if ($request_method = OPTIONS) {
return 204;
}
# Allow ajax requests from other domains and subdomains
add_header Access-Control-Allow-Origin $allow_origin;
add_header Access-Control-Allow-Methods $allow_methods;
add_header Access-Control-Allow-Headers $allow_headers;
# Your other config here...
}
}
If you're using PHP this basic set up may help.
default.conf
server {
listen 443 ssl;
server_name pgn-chess-data.local;
ssl_certificate /etc/nginx/ssl/pgn-chess-data.local.crt;
ssl_certificate_key /etc/nginx/ssl/pgn-chess-data.local.key;
ssl_ciphers EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH;
ssl_protocols TLSv1.1 TLSv1.2;
root /usr/share/nginx/pgn-chess-data/public;
client_max_body_size 20M;
location ~ ^/api/ {
try_files $uri /index.php$is_args$args;
}
location ~ ^/.+\.php(/|$) {
fastcgi_pass php_fpm:9000;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
}
public/index.php
<?php
require realpath(dirname(__FILE__)) .'/../src/bootstrap.php';
header("Access-Control-Allow-Origin: *");
header('Access-Control-Allow-Methods: GET, POST');
header("Access-Control-Allow-Headers: DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range");
switch (true) {
case '/api/query' === $_SERVER['REQUEST_URI'] && $_SERVER['REQUEST_METHOD'] === 'POST':
require APP_PATH . '/src/Api/Query.php';
exit;
case $_SERVER['REQUEST_METHOD'] === 'OPTIONS':
http_response_code(204);
exit;
default:
http_response_code(404);
exit;
}
Hello, I have fixed try_files
problem next way
server {
server_name api.domain.com;
set $cors "https://domain.com";
if ($http_origin ~* (/|\.)domain\.(com|dev)$) {
set $cors $http_origin;
}
add_header 'Access-Control-Allow-Origin' '$cors' always;
...
...
...
location / {
try_files $uri $uri/ /index.php$is_args$args;
}
}
For me something like this broke my preflight check for cors and therefor failing cors for some reason...
` gzip off;
proxy_set_header X-Forwarded-Ssl on;
client_max_body_size 50M;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Frame-Options SAMEORIGIN;
proxy_pass http://test.app_backend;
set $cors "";
if ($http_origin ~* (.*\.test.app|https://waves.exchange|.*\.test2.app|.*\.wallet.com)) {
set $cors "true";
}
if ($cors = "true") {
add_header 'Access-Control-Allow-Origin' "$http_origin" always;
add_header 'Access-Control-Allow-Credentials' 'true' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE' always;
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,signature,timestamp' always;
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always;
}
if ($request_method = 'OPTIONS') {
return 204;
}
`
work for me
Thanks for this. I think you want a
$
at the end of the regex on line 18. Without it my sitehttp://whitelist.address.one.evil.com
will pass and show up in theAccess-Control-Allow-Origin
header.