Question about Headers IIS reverse proxy with NGINX

Notice: Page may contain affiliate links for which we may earn a small commission through services like Amazon Affiliates or Skimlinks.

Albert Yang

Oct 26, 2017

I was wondering if someone could shed some light on the issue im having,

Currently i have working great NGINX as reverse proxy for my IIS

Im trying to get working the Strict-Transport-Security, X-Frame-Options, X-Content-Type-Options working my NGINX but it keeps showing that its not getting applied when i check

Analyse your HTTP response headers


#        listen 80;
   listen 443 ssl;

  ssl_certificate /etc/letsencrypt/live/;
        ssl_certificate_key /etc/letsencrypt/live/;
        ssl_dhparam /etc/ssl/certs/dhparam.pem;
        ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH';
        ssl_session_timeout 1d;
        ssl_session_cache shared:SSL:50m;
        ssl_stapling on;
       ssl_stapling_verify on;

## security headers
# Block loading in an iFrame
add_header X-Frame-Options SAMEORIGIN;
# Enforce HTTPS
add_header Strict-Transport-Security "max-age=31536000; includeSubdomains";
# Blocks hidden malicious scripts
add_header X-Content-Type-Options nosniff;
# Stops scripts from unknown sources
add_header X-XSS-Protection "1; mode=block";
# Content security policy
add_header Content-Security-Policy "default-src 'self' https: data: 'unsafe-inline' 'unsafe-eval';" always;
# Referal policy
add_header Referrer-Policy "origin-when-cross-origin" always;
# permision policy
add_header Feature-Policy "camera 'none'; microphone 'none'; geolocation 'none'" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;

        location ~ /.well-known {
        root /var/www/letsencrypt;
        allow all;
        location / {


#                headers setting

                proxy_set_header Host $host;

                proxy_set_header X-Real-IP $remote_addr;

                proxy_set_header X-Forwarded-For $remote_addr;

                proxy_set_header X-Forwarded-Proto $scheme;
                proxy_set_header X-Client-IP $remote_addr;


im going to assume

proxy_set_header Host $host; is what shows the header of the IIS?

Thank you


Well-Known Member
Apr 21, 2017
I suggest you look at output of wget -S -O/dev/null if you are emitting duplicate headers. Personally I like openresty (nginx with lua) because it contains the "headers-more" module (GitHub - openresty/headers-more-nginx-module: Set, add, and clear arbitrary output headers in NGINX http servers). Syntax is different:

server_tokens off;
more_clear_headers "X-Powered-By";
more_set_headers "X-Content-Type-Options: nosniff";
more_set_headers "X-Frame-Options: sameorigin";
more_set_headers "Strict-Transport-Security: max-age=15768000; includeSubDomains";

proxy_set_header is what gets passed to your IIS. Host for example is the $host which nginx got from the client, passing it on to the proxied IIS so if more than one site is configured, IIS will know which one the client wanted through that header.
Last edited:

Albert Yang

Oct 26, 2017
Thanks for the reply currently i ran the command and i got this
So to accomplish the headers correctly i would need openresty?
I currently complied NGINX with NAXSI running this, im not sure how would i compile for openresty

./configure \
--conf-path=/etc/nginx/nginx.conf \
--add-module=../naxsi-master/naxsi_src/ \
--error-log-path=/var/log/nginx/error.log \
--http-client-body-temp-path=/var/lib/nginx/body \
--http-fastcgi-temp-path=/var/lib/nginx/fastcgi \
--http-log-path=/var/log/nginx/access.log \
--http-proxy-temp-path=/var/lib/nginx/proxy \
--http-scgi-temp-path=/var/lib/nginx/scgi \
--http-uwsgi-temp-path=/var/lib/nginx/uwsgi \
--lock-path=/var/lock/nginx.lock \
--pid-path=/var/run/ \
--user=www-data \
--group=www-data \
--with-debug \
--with-compat \
--with-pcre-jit \
--with-http_ssl_module \
--with-http_stub_status_module \
--with-http_realip_module \
--with-http_auth_request_module \
--with-http_v2_module \
--with-http_dav_module \
--with-http_slice_module \
--with-threads \
--with-http_addition_module \
--with-http_gunzip_module \
--with-http_gzip_static_module \
--with-http_image_filter_module=dynamic \
--with-http_sub_module \
--with-http_xslt_module=dynamic \
--with-stream=dynamic \
--with-stream_ssl_module \
--with-mail=dynamic \
--with-mail_ssl_module \

HTTP request sent, awaiting response...
  HTTP/1.1 403 Forbidden
  Server: nginx
  Date: Mon, 04 Apr 2022 15:02:56 GMT
  Content-Type: text/html
  Content-Length: 1237
  Connection: keep-alive
  Content-Security-Policy: default-src 'self' https: data: 'unsafe-inline' 'unsafe-eval';
  Referrer-Policy: origin-when-cross-origin
  Feature-Policy: camera 'none'; microphone 'none'; geolocation 'none'
  Permissions-Policy: camera=(), microphone=(), geolocation=()
2022-04-04 10:02:57 ERROR 403: Forbidden.
Thank you


Well-Known Member
Apr 21, 2017
You need to study documentation deeper.

Read Module ngx_http_headers_module

"Adds the specified field to a response header provided that the response code equals 200, 201 (1.3.10), 204, 206, 301, 302, 303, 304, 307 (1.1.16, 1.0.13), or 308 (1.13.0). ... If the always parameter is specified (1.7.5), the header field will be added regardless of the response code."

Openresty is a "stable" self-contained nginx distribution with different and more modules activated. I use it on Arch Linux installed to /opt from AUR because I happened to have also a use for Lua. If you get the duplicate header problem, more_headers is the way to go and openresty already integrates it, so no need to compile everything from scratch yourself. On Arch "yay -S openresty" and after a couple minutes its compiled and ready to go.