-
-
Save rhfung/12fced0c159572f5207a2da5b6ecdab1 to your computer and use it in GitHub Desktop.
nginx configuration for CORS (Cross-Origin Resource Sharing), with an origin whitelist, and HTTP Basic Access authentication allowed
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# | |
# A CORS (Cross-Origin Resouce Sharing) config for nginx | |
# | |
# == Purpose | |
# | |
# This nginx configuration enables CORS requests in the following way: | |
# - enables CORS just for origins on a whitelist specified by a regular expression | |
# - CORS preflight request (OPTIONS) are responded immediately | |
# - Access-Control-Allow-Credentials=true for GET and POST requests | |
# - Access-Control-Max-Age=20days, to minimize repetitive OPTIONS requests | |
# - various superluous settings to accommodate nonconformant browsers | |
# | |
# == Comment on echoing Access-Control-Allow-Origin | |
# | |
# How do you allow CORS requests only from certain domains? The last | |
# published W3C candidate recommendation states that the | |
# Access-Control-Allow-Origin header can include a list of origins. | |
# (See: http://www.w3.org/TR/2013/CR-cors-20130129/#access-control-allow-origin-response-header ) | |
# However, browsers do not support this well and it likely will be | |
# dropped from the spec (see, http://www.rfc-editor.org/errata_search.php?rfc=6454&eid=3249 ). | |
# | |
# The usual workaround is for the server to keep a whitelist of | |
# acceptable origins (as a regular expression), match the request's | |
# Origin header against the list, and echo back the matched value. | |
# | |
# (Yes you can use '*' to accept all origins but this is too open and | |
# prevents using 'Access-Control-Allow-Credentials: true', which is | |
# needed for HTTP Basic Access authentication.) | |
# | |
# == Comment on spec | |
# | |
# Comments below are all based on my reading of the CORS spec as of | |
# 2013-Jan-29 ( http://www.w3.org/TR/2013/CR-cors-20130129/ ), the | |
# XMLHttpRequest spec ( | |
# http://www.w3.org/TR/2012/WD-XMLHttpRequest-20121206/ ), and | |
# experimentation with latest versions of Firefox, Chrome, Safari at | |
# that point in time. | |
# | |
# == Changelog | |
# | |
# based on: https://gist.github.com/algal/5480916 | |
# based on: https://gist.github.com/michiel/1064640 | |
# CORS | |
# specifically, this example allow CORS requests from | |
# scheme : http or https | |
# authority : any authority ending in ".mckinsey.com" | |
# port : nothing, or : | |
if ($http_origin ~* ((http|https)?://[^/]*\.newtechq\.com(:[0-9]+)?)) { | |
set $cors "true"; | |
} | |
# Nginx doesn't support nested If statements, so we use string | |
# concatenation to create a flag for compound conditions | |
# OPTIONS indicates a CORS pre-flight request | |
if ($request_method = 'OPTIONS') { | |
set $cors "${cors}options"; | |
} | |
# non-OPTIONS indicates a normal CORS request | |
if ($request_method = 'GET') { | |
set $cors "${cors}get"; | |
} | |
if ($request_method = 'POST') { | |
set $cors "${cors}post"; | |
} | |
if ($request_method = 'PUT') { | |
set $cors "${cors}put"; | |
} | |
if ($request_method = 'PATCH') { | |
set $cors "${cors}put"; | |
} | |
if ($request_method = 'DELETE') { | |
set $cors "${cors}delete"; | |
} | |
# if it's a GET, POST, PUT, DELETE, set the standard CORS responses header | |
if ($cors = "trueget") { | |
# Tells the browser this origin may make cross-origin requests | |
# (Here, we echo the requesting origin, which matched the whitelist.) | |
add_header 'Access-Control-Allow-Origin' "$http_origin"; | |
# Tells the browser it may show the response, when XmlHttpRequest.withCredentials=true. | |
add_header 'Access-Control-Allow-Credentials' 'true'; | |
# # Tell the browser which response headers the JS can see, besides the "simple response headers" | |
# add_header 'Access-Control-Expose-Headers' 'myresponseheader'; | |
} | |
if ($cors = "truepost") { | |
# Tells the browser this origin may make cross-origin requests | |
# (Here, we echo the requesting origin, which matched the whitelist.) | |
add_header 'Access-Control-Allow-Origin' "$http_origin"; | |
# Tells the browser it may show the response, when XmlHttpRequest.withCredentials=true. | |
add_header 'Access-Control-Allow-Credentials' 'true'; | |
# # Tell the browser which response headers the JS can see, besides the "simple response headers" | |
# add_header 'Access-Control-Expose-Headers' 'myresponseheader'; | |
} | |
if ($cors = "trueput") { | |
# Tells the browser this origin may make cross-origin requests | |
# (Here, we echo the requesting origin, which matched the whitelist.) | |
add_header 'Access-Control-Allow-Origin' "$http_origin"; | |
# Tells the browser it may show the response, when XmlHttpRequest.withCredentials=true. | |
add_header 'Access-Control-Allow-Credentials' 'true'; | |
# # Tell the browser which response headers the JS can see, besides the "simple response headers" | |
# add_header 'Access-Control-Expose-Headers' 'myresponseheader'; | |
} | |
if ($cors = "truepatch") { | |
# Tells the browser this origin may make cross-origin requests | |
# (Here, we echo the requesting origin, which matched the whitelist.) | |
add_header 'Access-Control-Allow-Origin' "$http_origin"; | |
# Tells the browser it may show the response, when XmlHttpRequest.withCredentials=true. | |
add_header 'Access-Control-Allow-Credentials' 'true'; | |
# # Tell the browser which response headers the JS can see, besides the "simple response headers" | |
# add_header 'Access-Control-Expose-Headers' 'myresponseheader'; | |
} | |
if ($cors = "truedelete") { | |
# Tells the browser this origin may make cross-origin requests | |
# (Here, we echo the requesting origin, which matched the whitelist.) | |
add_header 'Access-Control-Allow-Origin' "$http_origin"; | |
# Tells the browser it may show the response, when XmlHttpRequest.withCredentials=true. | |
add_header 'Access-Control-Allow-Credentials' 'true'; | |
# # Tell the browser which response headers the JS can see, besides the "simple response headers" | |
# add_header 'Access-Control-Expose-Headers' 'myresponseheader'; | |
} | |
# if it's OPTIONS, then it's a CORS preflight request so respond immediately with no response body | |
if ($cors = "trueoptions") { | |
# Tells the browser this origin may make cross-origin requests | |
# (Here, we echo the requesting origin, which matched the whitelist.) | |
add_header 'Access-Control-Allow-Origin' "$http_origin"; | |
# in a preflight response, tells browser the subsequent actual request can include user credentials (e.g., cookies) | |
add_header 'Access-Control-Allow-Credentials' 'true'; | |
# | |
# Return special preflight info | |
# | |
# Tell browser to cache this pre-flight info for 20 days | |
add_header 'Access-Control-Max-Age' 1728000; | |
# Tell browser we respond to GET,POST,PUT,DELETE,OPTIONS in normal CORS requests. | |
# | |
# Not officially needed but still included to help non-conforming browsers. | |
# | |
# OPTIONS should not be needed here, since the field is used | |
# to indicate methods allowed for "actual request" not the | |
# preflight request. | |
# | |
# GET,POST also should not be needed, since the "simple | |
# methods" GET,POST,HEAD are included by default. | |
# | |
# We should only need this header for non-simple requests | |
# methods (e.g., DELETE), or custom request methods (e.g., XMODIFY) | |
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS'; | |
# Tell browser we accept these headers in the actual request | |
# The bottom line: it seems there are headers needed for the | |
# web and CORS to work, which at the moment you should | |
# hard-code into Access-Control-Allow-Headers, although | |
# official specs imply this should not be necessary. | |
# | |
add_header 'Access-Control-Allow-Headers' 'Access-Control-Request-Method,Access-Control-Request-Headers,Cache,Pragma,Authorization,Accept,Accept-Encoding,Accept-Language,Host,Referer,Content-Length,Origin,DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type'; | |
# build entire response to the preflight request | |
# no body in this response | |
add_header 'Content-Length' 0; | |
# (should not be necessary, but included for non-conforming browsers) | |
add_header 'Content-Type' 'text/plain charset=UTF-8'; | |
# indicate successful return with no content | |
return 204; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment