[PATCH] Support X-Forwarded-Proto in redirects

Lukas Vacek lucas.vacek at gmail.com
Mon Mar 19 19:53:46 UTC 2018


# HG changeset patch
# User Lukas Vacek <lucas.vacek at gmail.com>
# Date 1521488415 -3600
#      Mon Mar 19 20:40:15 2018 +0100
# Node ID 0f9ed8bb5c9505f7a77271eed54e7b01b9b65a81
# Parent  413189f03c8d13d0d20bd5e44fa0e48e693badef
Support X-Forwarded-Proto in redirects

This patch adds a new configuration option x_forwarded_proto_in_redirect
(default off) to fill schema from X-Forwarded-Proto HTTP header in
URLs generated by nginx.

This is handy when running nginx behind SSL off-loading reverse proxy.

diff -r 413189f03c8d -r 0f9ed8bb5c95 src/http/ngx_http_core_module.c
--- a/src/http/ngx_http_core_module.c	Mon Mar 19 16:42:56 2018 +0300
+++ b/src/http/ngx_http_core_module.c	Mon Mar 19 20:40:15 2018 +0100
@@ -576,6 +576,13 @@
       offsetof(ngx_http_core_loc_conf_t, port_in_redirect),
       NULL },
 
+    { ngx_string("x_forwarded_proto_in_redirect"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+      ngx_conf_set_flag_slot,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      offsetof(ngx_http_core_loc_conf_t, x_forwarded_proto_in_redirect),
+      NULL },
+
     { ngx_string("msie_padding"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
       ngx_conf_set_flag_slot,
@@ -3401,6 +3408,7 @@
     clcf->absolute_redirect = NGX_CONF_UNSET;
     clcf->server_name_in_redirect = NGX_CONF_UNSET;
     clcf->port_in_redirect = NGX_CONF_UNSET;
+    clcf->x_forwarded_proto_in_redirect = NGX_CONF_UNSET;
     clcf->msie_padding = NGX_CONF_UNSET;
     clcf->msie_refresh = NGX_CONF_UNSET;
     clcf->log_not_found = NGX_CONF_UNSET;
@@ -3669,6 +3677,8 @@
     ngx_conf_merge_value(conf->server_name_in_redirect,
                               prev->server_name_in_redirect, 0);
     ngx_conf_merge_value(conf->port_in_redirect, prev->port_in_redirect, 1);
+    ngx_conf_merge_value(conf->x_forwarded_proto_in_redirect,
+                              prev->x_forwarded_proto_in_redirect, 0);
     ngx_conf_merge_value(conf->msie_padding, prev->msie_padding, 1);
     ngx_conf_merge_value(conf->msie_refresh, prev->msie_refresh, 0);
     ngx_conf_merge_value(conf->log_not_found, prev->log_not_found, 1);
diff -r 413189f03c8d -r 0f9ed8bb5c95 src/http/ngx_http_core_module.h
--- a/src/http/ngx_http_core_module.h	Mon Mar 19 16:42:56 2018 +0300
+++ b/src/http/ngx_http_core_module.h	Mon Mar 19 20:40:15 2018 +0100
@@ -385,6 +385,8 @@
     ngx_flag_t    absolute_redirect;       /* absolute_redirect */
     ngx_flag_t    server_name_in_redirect; /* server_name_in_redirect */
     ngx_flag_t    port_in_redirect;        /* port_in_redirect */
+    ngx_flag_t    x_forwarded_proto_in_redirect;
+                                           /* x_forwarded_proto_in_redirect */
     ngx_flag_t    msie_padding;            /* msie_padding */
     ngx_flag_t    msie_refresh;            /* msie_refresh */
     ngx_flag_t    log_not_found;           /* log_not_found */
diff -r 413189f03c8d -r 0f9ed8bb5c95 src/http/ngx_http_header_filter_module.c
--- a/src/http/ngx_http_header_filter_module.c	Mon Mar 19 16:42:56 2018 +0300
+++ b/src/http/ngx_http_header_filter_module.c	Mon Mar 19 20:40:15 2018 +0100
@@ -158,7 +158,7 @@
 {
     u_char                    *p;
     size_t                     len;
-    ngx_str_t                  host, *status_line;
+    ngx_str_t                  proto, host, *status_line;
     ngx_buf_t                 *b;
     ngx_uint_t                 status, i, port;
     ngx_chain_t                out;
@@ -343,12 +343,44 @@
             }
         }
 
-        port = ngx_inet_get_port(c->local_sockaddr);
+        proto.data = (u_char *)"http";
+        proto.len = 4;
 
-        len += sizeof("Location: https://") - 1
+#if (NGX_HTTP_SSL)
+        if (c->ssl) {
+            proto.data = (u_char *)"https";
+            proto.len = 5;
+        }
+#endif
+        if (clcf->x_forwarded_proto_in_redirect) {
+            part = &r->headers_in.headers.part;
+            header = part->elts;
+
+            for (i = 0; /* void */; i++) {
+                if (i >= part->nelts) {
+                    if (part->next == NULL) {
+                        break;
+                    }
+
+                    part = part->next;
+                    header = part->elts;
+                    i = 0;
+                }
+
+                if (ngx_strcasecmp(header[i].key.data,
+                        (u_char *)"X-Forwarded-Proto") == 0) {
+                    proto = header[i].value;
+                    break;
+                }
+            }
+        }
+
+        len += sizeof("Location: ") - 1 + proto.len + sizeof("://") - 1
                + host.len
                + r->headers_out.location->value.len + 2;
 
+        port = ngx_inet_get_port(c->local_sockaddr);
+        
         if (clcf->port_in_redirect) {
 
 #if (NGX_HTTP_SSL)
@@ -368,6 +400,7 @@
 
     } else {
         ngx_str_null(&host);
+        ngx_str_null(&proto);
         port = 0;
     }
 
@@ -518,14 +551,9 @@
 
         p = b->last + sizeof("Location: ") - 1;
 
-        b->last = ngx_cpymem(b->last, "Location: http",
-                             sizeof("Location: http") - 1);
-
-#if (NGX_HTTP_SSL)
-        if (c->ssl) {
-            *b->last++ ='s';
-        }
-#endif
+        b->last = ngx_cpymem(b->last, "Location: ", sizeof("Location: ") - 1);
+        
+        b->last = ngx_copy(b->last, proto.data, proto.len);
 
         *b->last++ = ':'; *b->last++ = '/'; *b->last++ = '/';
         b->last = ngx_copy(b->last, host.data, host.len);
diff -r 413189f03c8d -r 0f9ed8bb5c95 src/http/v2/ngx_http_v2_filter_module.c
--- a/src/http/v2/ngx_http_v2_filter_module.c	Mon Mar 19 16:42:56 2018 +0300
+++ b/src/http/v2/ngx_http_v2_filter_module.c	Mon Mar 19 20:40:15 2018 +0100
@@ -319,11 +319,45 @@
                 }
             }
 
+            ngx_str_t proto;
+            proto.data = (u_char* )"http";
+            proto.len = 4;
+
+#if (NGX_HTTP_SSL)
+            if (fc->ssl) {
+                proto.data = (u_char* )"https";
+                proto.len = 5;
+            }
+#endif
+
+            if (clcf->x_forwarded_proto_in_redirect) {
+                part = &r->headers_in.headers.part;
+                header = part->elts;
+
+                for (i = 0; /* void */; i++) {
+                    if (i >= part->nelts) {
+                        if (part->next == NULL) {
+                            break;
+                        }
+
+                        part = part->next;
+                        header = part->elts;
+                        i = 0;
+                    }
+
+                    if (ngx_strcasecmp(header[i].key.data,
+                            (u_char *)"X-Forwarded-Proto") == 0) {
+                        proto = header[i].value;
+                        break;
+                    }
+                }
+            }
+
+            location.len = proto.len + sizeof("://") - 1 + host.len +
+                           + r->headers_out.location->value.len;
+
             port = ngx_inet_get_port(fc->local_sockaddr);
 
-            location.len = sizeof("https://") - 1 + host.len
-                           + r->headers_out.location->value.len;
-
             if (clcf->port_in_redirect) {
 
 #if (NGX_HTTP_SSL)
@@ -346,13 +380,7 @@
                 return NGX_ERROR;
             }
 
-            p = ngx_cpymem(location.data, "http", sizeof("http") - 1);
-
-#if (NGX_HTTP_SSL)
-            if (fc->ssl) {
-                *p++ = 's';
-            }
-#endif
+            p = ngx_cpymem(location.data, proto.data, proto.len);
 
             *p++ = ':'; *p++ = '/'; *p++ = '/';
             p = ngx_cpymem(p, host.data, host.len);


More information about the nginx-devel mailing list