2

I have an instance of lighttpd 1.4.35 listening for https traffic and reverse-proxying it to a back-end server. I.e.,

                   .----------.            .----------.
client ---https--> | lighttpd | ---http--> | back-end |
       <--https--- |          | <--http--- | server   |
                   `----------'            `----------'

When I do an HTTP post to a proxied page (over https), the back-end server is returning a Location header with http instead of https.

Location:  http://lighttpd_url/some_page.htm

Is there a way lighttpd can rewrite the URL in the location header? I see in lighttpd 1.5.x, proxy-core has a rewrite-response directive, and I guess this is what it would look like:

 proxy-core.rewrite-response = (
      "Location" => ( "^http://xyz/(.*)" => "https://xyz/$1" ),
    )

But how do I rewrite the Location header in lighttpd 1.4.x?

3
  • Unfortunately, I don't think there's any way to do this without the rewrite-response option. Jun 26, 2014 at 20:35
  • @ShaneMadden: Thanks, I think you're correct after looking at the source code. It appears mod_rewrite doesn't get a chance to change the headers in mod_proxy responses.
    – indiv
    Jun 26, 2014 at 23:58
  • You could try to load mod_magnet before mod_proxy and use magnet.attract-raw-url-to to load a lua script which sets lighty.env["uri.scheme"] = "https", making mod_proxy send a X-Forwarded-Proto: https header, so the backend generates correct location headers.
    – Stefan
    Jun 27, 2014 at 15:02

1 Answer 1

2

As of 1.4.35, mod_proxy doesn't let you modify response headers. But I really needed the simple functionality of changing Location response headers from http to https, so I hacked it into mod_proxy.c.

Here is the patch in case it's useful to anyone else. After applying this patch and then rebuilding and installing from source, you can add proxy.force_https_location = 1 to your config file to enable the feature globally.

--- src/mod_proxy.c-orig    2014-06-26 14:33:50.000000000 -0700
+++ src/mod_proxy.c 2014-06-26 16:08:11.000000000 -0700
@@ -64,6 +64,7 @@
 typedef struct {
    array *extensions;
    unsigned short debug;
+   unsigned short force_https_location;

    proxy_balance_t balance;
 } plugin_config;
@@ -191,6 +192,7 @@
        { "proxy.server",              NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION },       /* 0 */
        { "proxy.debug",               NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION },       /* 1 */
        { "proxy.balance",             NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },      /* 2 */
+       { "proxy.force_https_location", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION },      /* 3 */
        { NULL,                        NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
    };

@@ -203,10 +205,12 @@
        s = malloc(sizeof(plugin_config));
        s->extensions    = array_init();
        s->debug         = 0;
+       s->force_https_location = 0;

        cv[0].destination = s->extensions;
        cv[1].destination = &(s->debug);
        cv[2].destination = p->balance_buf;
+       cv[3].destination = &(s->force_https_location);

        buffer_reset(p->balance_buf);

@@ -568,10 +572,13 @@
        int key_len;
        data_string *ds;
        int copy_header;
+       int is_location_header;

        ns[0] = '\0';
        ns[1] = '\0';

+       is_location_header = 0;
+
        if (-1 == http_response_status) {
            /* The first line of a Response message is the Status-Line */

@@ -614,6 +621,7 @@
            if (0 == strncasecmp(key, "Location", key_len)) {
                con->parsed_response |= HTTP_LOCATION;
            }
+           is_location_header = 1;
            break;
        case 10:
            if (0 == strncasecmp(key, "Connection", key_len)) {
@@ -635,7 +643,26 @@
                ds = data_response_init();
            }
            buffer_copy_string_len(ds->key, key, key_len);
-           buffer_copy_string(ds->value, value);
+
+           if (is_location_header && p->conf.force_https_location) {
+               const unsigned int http_prefix_len = 7; /* strlen("http://") */
+
+               if (0 == strncasecmp(value, "http://", http_prefix_len)) {
+                   buffer_copy_string(ds->value, "https://");
+                   buffer_append_string(ds->value, value + http_prefix_len);
+
+                   if (p->conf.debug) {
+                       log_error_write(srv, __FILE__, __LINE__, "sb", "forced Location to https: ", ds->value);
+                   }
+               }
+               else {
+                   buffer_copy_string(ds->value, value);
+               }
+           }
+           else
+           {
+               buffer_copy_string(ds->value, value);
+           }

            array_insert_unique(con->response.headers, (data_unset *)ds);
        }
@@ -873,6 +897,7 @@
    PATCH(extensions);
    PATCH(debug);
    PATCH(balance);
+   PATCH(force_https_location);

    /* skip the first, the global context */
    for (i = 1; i < srv->config_context->used; i++) {
@@ -892,6 +917,8 @@
                PATCH(debug);
            } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("proxy.balance"))) {
                PATCH(balance);
+           } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("proxy.force_https_location"))) {
+               PATCH(force_https_location);
            }
        }
    }

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .