-
-
Save Stanback/7145487 to your computer and use it in GitHub Desktop.
# | |
# CORS header support | |
# | |
# One way to use this is by placing it into a file called "cors_support" | |
# under your Nginx configuration directory and placing the following | |
# statement inside your **location** block(s): | |
# | |
# include cors_support; | |
# | |
# As of Nginx 1.7.5, add_header supports an "always" parameter which | |
# allows CORS to work if the backend returns 4xx or 5xx status code. | |
# | |
# For more information on CORS, please see: http://enable-cors.org/ | |
# Forked from this Gist: https://gist.github.com/michiel/1064640 | |
# | |
set $cors ''; | |
if ($http_origin ~ '^https?://(localhost|www\.yourdomain\.com|www\.yourotherdomain\.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, PUT, DELETE, OPTIONS' always; | |
add_header 'Access-Control-Allow-Headers' 'Accept,Authorization,Cache-Control,Content-Type,DNT,If-Modified-Since,Keep-Alive,Origin,User-Agent,X-Requested-With' always; | |
# required to be able to read Authorization header in frontend | |
#add_header 'Access-Control-Expose-Headers' 'Authorization' always; | |
} | |
if ($request_method = 'OPTIONS') { | |
# 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; | |
} |
My 2 cents with the fork of this gist https://gist.github.com/iver/4f9616631008ea3a27ba0b3e63135381
@mPanasiewicz Your solution works quite good but I preferred solution from @slavafomin since it does not send cors headers when there is no cors request (no Origin
header in request)
Ok so after some more testing I still had some errors concerning not allowed headers. After reading https://fetch.spec.whatwg.org/#http-cors-protocol and based on previous comments, I've created this configuration below. It works for me because:
- I needed to allow all origins
- I don't care about headers that are sent
- I needed to allow credentials (to send cookies in CORS requests)
- I want to send CORS headers only for CORS requests
Access-Control-Allow-Origin
cannot be*
because CORS disallow this with credentials enabled
location ~ ^/index\.php(/|$) {
set $cors '';
# Match anything not empty
if ($http_origin ~ '.+') {
set $cors 'origin_matched';
}
# Preflight requests
if ($request_method = OPTIONS) {
set $cors '${cors} & preflight';
}
# standard CORS request
if ($cors = 'origin_matched') {
# for standard CORS requests only those headers are required
add_header Access-Control-Allow-Origin $http_origin always;
add_header Access-Control-Allow-Credentials 'true' always;
}
# preflight CORS request
if ($cors = 'origin_matched & preflight') {
# standard CORS headers are required
add_header Access-Control-Allow-Origin $http_origin always;
add_header Access-Control-Allow-Credentials 'true' always;
# additional headers are required and need to match exactly what was sent in CORS request
add_header Access-Control-Allow-Methods $http_access_control_request_method always;
add_header Access-Control-Allow-Headers $http_access_control_request_headers always;
add_header Content-Type text/plain;
add_header Content-Length 0;
return 204;
}
fastcgi_pass ${NGINX_FASTCGI_PASS};
fastcgi_split_path_info ^(.+\.php)(/.*)$;
include fastcgi_params;
fastcgi_param REQUEST_URI $uri;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param HTTPS off;
}
@piotrekkr Maybe you're right but there is one big disadventage which is described here:
nginx if is evil My configuration avoids the if conditions. But feel free to decide what suits best for you ;-)
@mPanasiewicz Yeah I've read about this but as they wrote:
It is important to note that the behaviour of if is not inconsistent, given two identical requests it will not randomly fail on one and work on the other, with proper testing and understanding ifs ‘’‘can’‘’ be used. The advice to use other directives where available still very much applies, though.
and yes it works for me so I'll stick to this :)
A mystery for me is why for so many years, there is no easy, build in solution in Nginx to handle this?
Ok, I've played a little with nginx config and I think I've managed to avoid if
in location
block. Effect is similar to previous config. This is my new config:
server {
set $cors_credentials '';
set $cors_content_type '';
set $cors_content_length '';
if ($http_origin ~ '.+') {
set $cors_credentials 'true';
}
if ($request_method = OPTIONS) {
set $cors_content_type 'text/plain';
set $cors_content_length '0';
}
# empty header will not be added
add_header Access-Control-Allow-Origin $http_origin always;
add_header Access-Control-Allow-Credentials $cors_credentials always;
add_header Access-Control-Allow-Methods $http_access_control_request_method always;
add_header Access-Control-Allow-Headers $http_access_control_request_headers always;
add_header Content-Type $cors_content_type;
add_header Content-Length $cors_content_length;
if ($request_method = OPTIONS) {
return 204;
}
location / {
return 200 '==== OK ====' ;
}
}
I'm currently testing as an ingress annotation, should work fine.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/server-snippet: |
set $cors_credentials '';
set $cors_content_type '';
set $cors_content_length '';
if ($http_origin ~ '.+') {
set $cors_credentials 'true';
}
if ($request_method = OPTIONS) {
set $cors_content_type 'text/plain';
set $cors_content_length '0';
}
# empty header will not be added
add_header Access-Control-Allow-Origin $http_origin always;
add_header Access-Control-Allow-Credentials $cors_credentials always;
add_header Access-Control-Allow-Methods $http_access_control_request_method always;
add_header Access-Control-Allow-Headers $http_access_control_request_headers always;
add_header Content-Type $cors_content_type;
add_header Content-Length $cors_content_length;
if ($request_method = OPTIONS) {
return 204;
}
location / {
return 200 '==== OK ====' ;
}
I enabled a CORS in NGINX.Config. But still CORS is not working.
Please let me now where i am missing. Appreciate an input on this.
server {
root /usr/share/nginx/www;
index index.html index.htm;
location /grafana/ {
rewrite ^/grafana/(.*) /$1 break;
proxy_set_header Host $http_host;
proxy_pass http://grafana;
if ($http_origin ~ '^https?://(example\.com/grafana/\?orgId=1)$') {
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, PUT, DELETE, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'Accept,Authorization,Cache-Control,Content-Type,DNT,If-Modified-Since,Keep-
Alive,Origin,User-Agent,X-Requested-With' always;
# required to be able to read Authorization header in frontend
#add_header 'Access-Control-Expose-Headers' 'Authorization' always;
}
if ($request_method = 'OPTIONS')
{
add_header 'Access-Control-Allow-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-CustomHeader,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;
}
if ($request_method = 'POST') {
add_header 'Access-Control-Allow-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-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-
Since,Cache-Control,Content-Type';
}
if ($request_method = 'GET') {
add_header 'Access-Control-Allow-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-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-
Since,Cache-Control,Content-Type';
}
}
@RohitSharma2k122 What exactly is browser saying? Inside error message there should be explanation why CORS request was blocked. Can you post it? Also you can test cors requests with curl command like this (add some headers that are normally send etc):
curl -i -X OPTIONS -H 'Origin: http://frontend-app-host.com' https://backend-host.com
Hi @Stanback @mPanasiewicz @piotrekkr
Error is browser after enabled cors
Access to XMLHttpRequest at 'http://example.com/grafana/d/ZmqS29WVk/ade?orgId=1&from=now-2y&to=now&random=1662345172170Mon%20Sep%2005%202022%2008:02:52%20GMT+0530%20(India%20Standard%20Time)' from origin 'http://localhost:4200' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: Redirect is not allowed for a preflight request.
Please let me now where i am missing. Appreciate an input on this.
Cors Configuration
set $cors '';
# Match anything not empty
if ($http_origin ~ '.+') {
set $cors 'origin_matched';
}
# Preflight requests
if ($request_method = OPTIONS) {
set $cors '${cors} & preflight';
}
# standard CORS request
if ($cors = 'origin_matched') {
# for standard CORS requests only those headers are required
add_header Access-Control-Allow-Origin $http_origin always;
add_header Access-Control-Allow-Credentials 'true' always;
}
# preflight CORS request
if ($cors = 'origin_matched & preflight') {
# standard CORS headers are required
add_header Access-Control-Allow-Origin $http_origin always;
add_header Access-Control-Allow-Credentials 'true' always;
# additional headers are required and need to match exactly what was sent in CORS request
add_header Access-Control-Allow-Methods $http_access_control_request_method always;
add_header Access-Control-Allow-Headers $http_access_control_request_headers always;
add_header Content-Type text/plain;
add_header Content-Length 0;
return 204;
}
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-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-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-
Since,Cache-Control,Content-Type';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain charset=UTF-8';
add_header 'Content-Length' 0;
return 204;
}
if ($request_method = 'POST') {
add_header 'Access-Control-Allow-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-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
}
if ($request_method = 'GET') {
add_header 'Access-Control-Allow-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-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
}
Hello @RohitSharma2k122 . Seems like error message indicates problem: Redirect is not allowed for a preflight request
. Probably, when browser asks for CORS headers using OPTIONS
method it gets redirect response (http => https maybe?).
As I said, you can test those responses by
- enabling OPTIONS request in browser dev tools
- copy OPTION request as CURL (right click menu in networking tab in dev tools)
- Adding
-i
option tocurl
command copied before and running it in cli likecurl -i .......
It will show you response headers and body of OPTIONS request made by browser.
Btw, you probably should post this question on stackoverflow and not here.
Thanks you so much @piotrekkr
CORS issue is resolved by adding proxy_set_header Authorization in location tab.
server {
root /usr/share/nginx/www;
index index.html index.htm;
location /grafana/
{
include cors-settings.conf;
rewrite ^/grafana/(.*) /$1 break;
proxy_set_header Host $http_host;
proxy_pass http://grafana;
proxy_set_header Authorization "Basic YWRtaW";
}
Hello. @piotrekkr
Can't make CORS work. Can you help me?
Here is my configuration of Nginx without CORS params.
The backend is HTTP and the Frontend is HTTPS
server {
listen 80;
server_name example-frontend.com www.example-frontend.com;
return 301 https://example-frontend.com$request_uri;
}
server {
listen 443 ssl http2;
server_name example-frontend.com www.example-frontend.com;
# Configuration for SSL/TLS certificates
ssl_certificate /etc/letsencrypt/ssl/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/ssl/privkey.pem;
ssl_stapling on;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off;
# Disable insecure TLS versions
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers EECDH:+AES256:-3DES:RSA+AES:RSA+3DES:!NULL:!RC4;
# Logs
access_log /var/log/nginx/nginx.access.log;
error_log /var/log/nginx/nginx.error.log;
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
location /api/ {
proxy_set_header X-Forwarded-Host $host:$server_port;
proxy_set_header X-Forwarded-Server $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_pass http://example-backend:8080/;
}
location / {
root /app;
try_files $uri $uri/ /index.html =404;
}
}
Hello guys!
We have got working config only with the following trick:
`
if ( $request_method !~ ^(GET|POST|HEAD|OPTIONS|PUT|PATCH|DELETE)$ ) { return 444; } set $origin $http_origin; if ($origin !~ '^https?://(subdom1|subdom2)\.yourdom\.zone$') { set $origin 'https://default.yourdom.zone'; } if ($request_method = 'OPTIONS') { add_header 'Access-Control-Allow-Origin' "$origin" always; add_header 'Access-Control-Allow-Methods' 'GET, POST, PATCH, PUT, DELETE, OPTIONS' always; add_header 'Access-Control-Allow-Headers' 'Content-Type, Accept, Authorization' always; add_header 'Access-Control-Allow-Credentials' 'true' always; add_header Access-Control-Max-Age 1728000; add_header Content-Type 'text/plain charset=UTF-8'; add_header Content-Length 0; return 204; } if ($request_method ~ '(GET|POST|PATCH|PUT|DELETE)') { add_header Access-Control-Allow-Origin "$origin" always; add_header Access-Control-Allow-Methods 'GET, POST, PATCH, PUT, DELETE, OPTIONS' always; add_header Access-Control-Allow-Headers 'Content-Type, Accept, Authorization' always; add_header Access-Control-Allow-Credentials true always; }
`
if block can be implemented with map may be it can help somebody
works, the best solution so far!!!
one silly question: I have nginx ingress which I installed using helm charts. Any idea where to put this annotation or snippet???
Curtly facing issue "Access to XMLHttpRequest at 'https://xxxxx/tenant/dashboard/ml/project?startDate=2021-11-23T09%3A37%3A16-06%3A00&endDate=2022-11-23T09%3A37%3A16-06%3A00&filterby=InProgress&page=1&offset=5' from origin 'https://xxxxxxxxx.io' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource."
I am trying to do the same as you.
My BE is api.defaultseo.com, and FE defaultseo.com
When I log in from the FE, the refreshToken and Token are set on the BE only
I want to send the tokens to the FE on the response header
so when the FE requests access to a restricted BE route, FE sends the Token & Refresh Token to the BE.
Would you be able to help me fix the above? I also use MyVesta control panel to host the two websites on a droplet with Digital Ocean.
+1 for @mPanasiewicz 👍🏼
I am trying to do the same as you. My BE is api.defaultseo.com, and FE defaultseo.com When I log in from the FE, the refreshToken and Token are set on the BE only I want to send the tokens to the FE on the response header so when the FE requests access to a restricted BE route, FE sends the Token & Refresh Token to the BE.
Would you be able to help me fix the above? I also use MyVesta control panel to host the two websites on a droplet with Digital Ocean.
I figure this out. It was not related to Nginx
It was on the cookies I just added the cookie attribute: domain: 'defaultseo.com' for example that was my issue.
I'm just familiarizing myself with nginx configurations, but apparently
- if You use an
add_header
directive in anif
statement, no otheradd_header
from outside theif
seems to apply - empty strings used in
add_header
values aren't added/applied to the response
With these in mind, i only could made my case work (different methods allowed for different origins) was defining only variables inside the if
statements.
set $allowed_headers '';
set $allowed_methods '';
set $allowed_credentials '';
set $allowed_origin '';
set $type '';
set $length '';
if ( $http_origin ~ "https://put-enabled" ) {
set $allowed_origin $http_origin;
set $allowed_methods 'OPTIONS, PUT';
set $allowed_credentials false;
}
if ( $http_origin ~ "https://delete-enabled" ) {
set $allowed_headers 'content-type, X-Requested-With';
set $allowed_origin $http_origin;
set $allowed_methods 'OPTIONS, DELETE';
set $allowed_credentials true;
}
if ($request_method = OPTIONS) {
set $allowed_headers 'content-type';
set $type 'text/plain';
set $length 0;
}
add_header Access-Control-Allow-Origin $allowed_origin always;
add_header Access-Control-Allow-Methods $allowed_methods always;
add_header Access-Control-Allow-Headers $allowed_headers always;
add_header Access-Control-Allow-Credentials $allowed_credentials always;
add_header Content-Type $type;
add_header Content-Length $length;
if ($request_method = OPTIONS) {
return 204;
}
if ($request_method = POST) {
return 404;
}
If You wonder why the POST returns a 404, it's because the navigator (chrome at least) ignores the Access-Control-Allow-Methods
for POST requests. 🤷
I'm just familiarizing myself with nginx configurations, but apparently
* if You use an `add_header` directive in an `if` statement, no other `add_header` from outside the `if` seems to apply
That's because IF is evil
Having enabled the snippet, I scanned my site with nuclei and discovered that the regex '^https?://(localhost|www\.yourdomain\.com|www\.yourotherdomain\.com)'
allows for suffixing the domain with anything, as in e.g. www.yourdomain.com.honeypot.com. I amended the regex with a '$' at the end (as in www\.yourotherdomain\.com)$'
and it was smooth sailing.
mPanasiewicz Works like a charm! thanks
Hello @mPanasiewicz & @piotrekkr i saw you master niginx. Thank you for your good job I have a vps server with nginx and tomcat10..17, my frontend is not hosted on my vps. i have deployed my api java and test it throught postman and everything work but while i tried to fetch data from the frontend android and http this issue appear
Access to fetch at 'https://api.domain.com/directives/api/v1/file=' from origin 'https://domain.dev/' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
y81mcnb3w9p5cep8
My complete script that works both on my website and localhost:
In this script, my server is
blog.mywebsite.com
. It enabled CORSmywebsite.com
andlocalhost
to access requested resource.server { listen 443 ssl http2; listen [::]:443 ssl http2; server_name blog.mywebsite.com; root /var/www/ghost/system/nginx-root; ssl_certificate /etc/letsencrypt/blog.mywebsite.com/fullchain.cer; ssl_certificate_key /etc/letsencrypt/blog.mywebsite.com/blog.mywebsite.com.key; include /etc/nginx/snippets/ssl-params.conf; set $cors_origin ""; set $cors_cred ""; set $cors_header ""; set $cors_method ""; if ($http_origin ~ '^https?://(localhost|mywebsite\.com)$') { set $cors_origin $http_origin; set $cors_cred true; set $cors_header $http_access_control_request_headers; set $cors_method $http_access_control_request_method; } add_header Access-Control-Allow-Origin $cors_origin; add_header Access-Control-Allow-Credentials $cors_cred; add_header Access-Control-Allow-Headers $cors_header; add_header Access-Control-Allow-Methods $cors_method; location / { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Real-IP $remote_addr; proxy_set_header Host $http_host; proxy_pass http://127.0.0.1:2368; } location ~ /.well-known { allow all; } client_max_body_size 50m; }Your solution more or less worked for me using nginx version 1.12.2 Not sure which version of nginx you were using, but I couldn't run add_headers in my server block, but could in my location block which worked fine. Thanks.
add_header can work in http context .see http://nginx.org/en/docs/http/ngx_http_headers_module.html
worked like charm.
add_header can work in http context .see http://nginx.org/en/docs/http/ngx_http_headers_module.html