NGINX: add_header for GET not working - nginx

The following (simplified) NGINX config results in OPTIONS calls correctly getting the Access-Control-Allow-Origin header headers in return, but GET not:
location / {
if ($request_method = OPTIONS ) {
add_header 'Access-Control-Allow-Origin' '$http_origin' always;
return 204;
}
add_header 'Access-Control-Allow-Origin' '$http_origin' always;
try_files $uri #proxy_to_app;
}
location #proxy_to_app {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://app_server;
}
I've tried every combination in the location block, but nothing works, the only solution I could think of that works is moving the this line into the location #proxy_to_app section:
add_header 'Access-Control-Allow-Origin' '$http_origin' always;
It's like that proxy_to_app block is removing the added headers again.

As Alexey Ten stated, I moved add_header into location #proxy_to_app

Related

Correct proxy configuration for nginx server to access rest api

I have a nginx-server configured as follows:
server {
listen 3000;
listen [::]:3000;
server_name .+;
add_header Strict-Transport-Security "max-age=15552000; includeSubDomains" always;
...
index index.html;
root /usr/share/nginx/html;
location /cam/ {
proxy_pass http://cam:8000/;
}
location /api {
proxy_pass https://some_ip:some_port;
proxy_pass_request_headers on;
proxy_set_header X-API-KEY xxxxx;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $server_name;
proxy_ssl_server_name on;
}
location /share/ {
alias /usr/src/share;
}
location / {
try_files $uri $uri/ /index.html;
}
add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range' always;
client_max_body_size 1M;
keepalive_timeout 10;
}
For displaying my frontend, this configuration works fine. The proxy for the cam is also working correctly.
I am getting problems in the configuration of the proxy for the api. The api has a sanity-check under "https://some_ip:some_port", which just returns "200: ok". Otherwise I should be able to make request using a fetch request such as
const res = await fetch(`api/subaddress/${system_id.toString()}/base`, {
method: 'POST',
headers: {
'Content-type': 'application/json',
'X-API-KEY': xxxxx,
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'HEAD, GET, POST, PUT, PATCH, DELETE',
'Access-Control-Allow-Headers': 'Origin, Content-Type, X-Auth-Token'
},
body: JSON.stringify(data_body),
});
Which, if successfull, returns "202: ok".
If I make such a request using curl, it works just fine (both of the requests), so the api should not be the problem.
At the current configuration, I get a "200: ok", which tells me that I have connected to the api, but the proxy does not take in account my subaddresses.
Besides the given configuration, I tried the following:
location /api {
proxy_pass https://some_ip:some_port$request_uri;
...
}
which always gives me "502: bad gateway"
location /api/ {
proxy_pass https://some_ip:some_port/;
...
}
which always gives me "403: forbidden"
location /api {
rewrite ^/api/(.*) /$1 break;
proxy_pass https://some_ip:some_port/$1;
...
}
which causes nginx to crash, as it tells me that it cannot resolve it. I would like to avoid using a resolver, unless definitly necessary (I have tried it using a resolver before too and it did not work either).
What is the correct way to accomplish this?
I solved this. Apparently the 403: forbidden was caused my some of the configurations of the proxy. After removing those, I did not get that error anymore.
In the end I used
location /api {
rewrite ^/api/(.*) /$1 break;
proxy_pass https://some_ip:some_port/$1;
...
}
which I made work by putting
resolver 1.1.1.1 valid=30s;
at the top of the configuration file.

Nginx how to set proxy pass using map based on the host and location

Currently I setting proxy pass dynamicaly using map directive. Now I would like for a specifc location /dcs/ I would like to set a different proxy pass rather than one mentioned through map.conf and proxy_pass.map. Is there a way to say use different map for a specific location /dcs/.
Should just use If stantement like if host is dev-api.mysite.com then set proxypass to http://abcdef.com. Is this the only option.
MySite.conf
server {
listen 80;
server_name ~^(api|dev-api|staging-api)\.mysite\.com$;
location / {
proxy_set_header Cookie "";
add_header Strict-Transport-Security "max-age=0; includeSubDomains" always;
proxy_hide_header 'Access-Control-Allow-Origin';
add_header 'Access-Control-Allow-Origin' '*' always;
include /etc/nginx/conf.d/headers.inc;
}
location /dcs/ {
set $proxy_host $host;
set $proxy_pass Set-different-host based of dev,staging,prod;
proxy_set_header Cookie "";
add_header Strict-Transport-Security "max-age=0; includeSubDomains" always;
proxy_hide_header 'Access-Control-Allow-Origin';
add_header 'Access-Control-Allow-Origin' '*' always;
include /etc/nginx/conf.d/headers.inc;
}
}
maps.conf
map $host $proxy_pass {
hostnames;
include /etc/nginx/conf.d/proxy_pass.map;
}
proxy_pass.map
# mysite.com
api.mysite.com myst.plat:8080;
staging-api.mysite.com staging.myst.plat:8081;
dev-api.mysite.com dev.plat:8080;
headers.inc
proxy_pass $upstream_proto://$proxy_pass;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header X-Forwarded-Proto $real_scheme;
proxy_set_header X-Forwarded-Scheme $real_scheme;
proxy_set_header Host $proxy_host;
proxy_ssl_session_reuse on;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-GeoIP-Country-Code $geoip_country_code;
proxy_set_header X-Site-Id $site_id;
add_header X-Cache-Time $date_gmt;
add_header X-Cache-Date $upstream_http_date;
add_header X-Proxy-Cache $upstream_cache_status;
# add_header X-Site-Id $site_id;
You can use several chained map blocks:
map $host $proxy_by_host {
hostnames;
include /etc/nginx/conf.d/proxy_pass.map;
}
map $uri $proxy_pass {
~^/dcs/ abcdef.com;
default $proxy_by_host;
}
Update
Looks like I slightly misunderstood your question, to use two different map blocks depending on request URI, use something like
map $host $proxy_by_host {
hostnames;
include /etc/nginx/conf.d/proxy_pass.map;
}
map $host $dcs_proxy_by_host {
hostnames;
include /etc/nginx/conf.d/dcs_proxy_pass.map;
}
map $uri $proxy_pass {
~^/dcs/ $dcs_proxy_by_host;
default $proxy_by_host;
}
This way if all the other parameters for your locations are equal, you can use only one location / { ... } for all requests.

How to check substring using regular expression in Nginx config?

I try to check some parameters in request. Here is my url:
http://localhost:8080/wms?SERVICE=WMS&VERSION=1.3.0&REQUEST=GetMap&LAYERS=004C0000064F&
STYLES=&WIDTH=256&HEIGHT=256&FORMAT=image%2fjpeg&CRS=EPSG%3a100000&DPI=96&
MAP_RESOLUTION=96&FORMAT_OPTIONS=dpi%3a96&
BBOX=1530569.52624839870259166%2c524135.21126760687911883%2c1531064.27656850102357566%2c524629.96158770937472582
I trying to get REQUEST parameter. Here is my nginx 1.12.1 config:
server {
listen 8080;
server_name 127.0.0.1 localhost;
set $site_backend localhost:56297;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
location /favicon.ico {
error_page 403 404 = #tomcat_static_mapping;
}
location ~* /wms {
internal;
add_header URI $request_uri;
add_header X-debug-message1 "$request_uri" always;
if ($request_uri ~* REQUEST=([^&]*)) {
add_header X-debug-message2 "hi" always;
set $requesttype $1;
}
}
}
And in browser i got header:
X-debug-message1: /wms?SERVICE=WMS&VERSION=1.3.0&REQUEST=GetMap&LAYERS=004C0000064F&STYLES=&WIDTH=256&HEIGHT=256&FORMAT=image%2fjpeg&CRS=EPSG%3a100000&DPI=96&MAP_RESOLUTION=96&FORMAT_OPTIONS=dpi%3a96&BBOX=1530569.52624839870259166%2c524135.21126760687911883%2c1531064.27656850102357566%2c524629.96158770937472582
But not get X-debug-message2 header. I check regular expression here https://rubular.com/ and it's found match and return GetMap as like i want.
What can be wrong here?
Something is not complete / matching in your post. I got X-debug-message2: hi only which does match to how nginx has to behave:
These directives are inherited from the previous level if and only if there are no add_header directives defined on the current level
For more intuitive outcome, use Headers-More module.
more_set_headers "URI: $request_uri";
more_set_headers 'X-debug-message1: "$request_uri"';
location ~* /wms {
if ($request_uri ~* REQUEST=([^&]*)) {
more_set_headers 'X-debug-message2: hi';
set $requesttype $1;
}
}

How do multiple if in the same location block interact?

We have an hard time understanding how do multiple if statement works inside nginx:
We have a if statement that is supposed to set CORS headers when the request come from a certain domain
We have an if statement that is supposed to set some headers when the request method is OPTION.
However, when the request both has method OPTIONS and come from a certain domain, the CORS headers are not set. How do multiple if statement works in a single location context in Nginx?
server {
listen 127.0.0.1:80;
server_name myserver.com;
set $cors '';
if ($http_origin ~ '^https?://*.\.com') {
set $cors 'true';
}
location / {
proxy_pass http://myserver:9000;
if ($cors = 'true' ){
add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range' always;
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' 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;
}
### force timeouts if one of backend is died ##
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
### Set headers ####
proxy_set_header Accept-Encoding "";
proxy_set_header Host $host:$server_port;
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;
add_header Front-End-Https on;
proxy_redirect off;
}
}
The add_header directive behaves a bit differently to other Nginx directives as it does not inherit from another configuration block if it is included in the current block.
There are a few approaches depending upon how much granularity you need in deciding what headers to add where, you could use a map directive but as it looks like you only have 3 sets of headers, one for all connections and then your two conditional sets then for simplicity you can just move your ifs to the server block.
You have quite a few headers to add, so for clarity I would save each set in it's own .conf file. Then in your server block you already have one if directive, so change that and add your other if statement, and include your proxy headers you want to be set for everyone in the server block too:
include headers/proxy-headers.conf;
if ($http_origin ~ '^https?://*.\.com') {
include headers/cors-headers.conf;
}
if ($request_method = 'OPTIONS') {
include headers/options-headers.conf;
}

Nginx proxy_pass missing error body

Below is a pretty standard nginx proxy_pass setup:
server {
listen 80;
server_name ireport.jungdigital.com;
access_log /var/log/nginx/ireport.access.log;
root /var/www/ireport.jungdigital.com/dist;
index index.html index.htm;
location / {
}
location /api/ {
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Methods' 'GET, PUT, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Reques
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, PUT, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Reques
}
if ($request_method = 'PUT') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Methods' 'GET, PUT, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Reques
}
if ($request_method = 'GET') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Methods' 'GET, PUT, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Reques
}
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-NginX-proxy true;
proxy_set_header Host ireport.somehost.org;
proxy_pass http://ireport_dyndns/api/;
proxy_ssl_session_reuse off;
proxy_redirect off;
}
}
The API that I'm proxying returns response body's that contain error information for 400, 404, and 500 error codes. For example, on a 404, my response body might look like:
{
"errorCode": "TOKEN_NOT_FOUND",
"errorMessages": [
"Could not find a matching authorization token."
]
}
If I perform the request without the proxy, I get the response bodies for the errors.
If I use the nginx proxy, for some reason the response bodies are swallowed by nginx and I can't even see a response at all in my web browser Network tab.
Is there a way to tell Nginx to return response bodies for error codes in a proxy_pass?
I run into same question recently.
And the last anwser is that: add proxy header Upgrade
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
My full config as following:
upstream cloud-api {
server 127.0.0.1:8089;
}
client_max_body_size 20M;
client_header_buffer_size 8k;
large_client_header_buffers 4 16k;
server {
listen 8001;
access_log /data/senseid-cloud-log/senseid-cloud-api-access.log;
error_log /data/senseid-cloud-log/senseid-cloud-api-error.log warn;
location / {
proxy_http_version 1.1;
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 Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_read_timeout 300s;
proxy_pass http://cloud-api;
}
}
And you could catch 500/401 .etc error body
Details : http://nginx.org/en/docs/http/websocket.html
Hope to give you some help.
See How to add a response header on nginx when using proxy_pass?
Per Alexey's comment - "browsers need headers to allow access to response body. I guess, you need always flag for add_header"
Since nginx 1.7.5 you can use the keyword always to include headers even in error responses - so you can setup your nginx.conf as follow:
-
server {
server_name .myserver.com
location / {
proxy_pass http://mybackend;
add_header X-Upstream $upstream_addr always;
}
}
Today I move Laravel 5.2 from IIS 8.5 to Ubuntu 16.04 (Nginx 1.10.0 - PHP-FPM 7.0.10), I have same problem. The body response from server are always empty when request from Angular2. But the postman still get body on response.
So that must be problem with request headers.
This config help me solved problem above:
add_header 'Access-Control-Allow-Origin' '*' 'always';
add_header 'Access-Control-Allow-Credentials' 'true' 'always';
add_header 'Access-Control-Allow-Methods' 'GET, POST, DELETE, PUT, OPTIONS, HEAD' 'always';
add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type, X-Requested-With' 'always';
Third parameters for add_header only available in recent nginx version.

Resources