1

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?

  • Unfortunately, I don't think there's any way to do this without the rewrite-response option. – Shane Madden Jun 26 '14 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 '14 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 '14 at 15:02
1

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);
            }
        }
    }

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.