Project

General

Profile

[Solved] Problem with conditionals and hostnames

Added by flynn about 2 months ago

I use the conditional $HTTP["host"] often twice (or more) for the same hostname, but in different combinations or regular expressions.

I observed, that some configuration directives like alias.url += are later ignored for only one of these hostnames.

Example:
  • first $HTTP["host"] conditional for certificates:
    $HTTP["host"] =~ "^(a|b|c|d)\.bla\.de$" { 
      ssl.pemfile = "bla/fullchain.pem" 
      ssl.privkey = "bla/privkey.pem" 
      ssl.stapling-file = "bla/pem.ocsp" 
    }
    $HTTP["host"] =~ "^(a|b|c|d)\.blabla\.de$" { 
      ssl.pemfile = "blabla/fullchain.pem" 
      ssl.privkey = "blabla/privkey.pem" 
      ssl.stapling-file = "blabla/pem.ocsp" 
    }
    
  • later second $HTTP["host"] condittional for logfile, alias, ...
    $HTTP["host"] =~ "^a\.(bla|blabla)\.de$" {
      $HTTP["scheme"] == "https" {
        server.document-root = "/var/www/a/" 
        accesslog.filename = "/var/log/lighttpd/a-access.log" 
        alias.url += ...
      }
    }
    

Now
- the directives server.document-root and accesslog.filename are active for both hostnames
- the directive alias.url is ignored/does not work for the hostname a.bla.de (return code 404), but works for a.blabla.de (return core 200), which is second in the first conditional block!

As workaround I have to comment out/deactivate the conditional $HTTP["scheme"] == "https", then it works for both hostnames again.

Is this wrong configured, not supported or a bug?


Replies (6)

RE: Problem with conditionals and hostnames - Added by gstrauss about 2 months ago

It sounds like this may be a known limitation / design choice.

You did not share your entire config, but there may be an alias.url directive in a conditional later in the config which matches the request and overrides the "later second $HTTP["host"] conditional" you posted above.

See Configuration: File Syntax - Conditional Configuration Merging
The lighttpd configuration is parsed at startup and then is static. The static configuration is evaluated against each request, but += across configuration conditionals is not dynamically recalculated for each request.

Nested conditionals may work with += since at startup, a value in an outer conditional can be copied and added to in a nested conditional, and then part the static parsed configuration.
The same conditional repeated at the same nesting level within the same conditional block gets merged into a single conditional, IIRC.

What happens if you reverse the nested conditions?

$HTTP["scheme"] == "https" {
  $HTTP["host"] =~ "^a\.(bla|blabla)\.de$" {
    server.document-root = "/var/www/a/" 
    accesslog.filename = "/var/log/lighttpd/a-access.log" 
    alias.url += ...
  }
}

RE: Problem with conditionals and hostnames - Added by flynn about 2 months ago

Reverse the nested conditions solves the problem - thank you.

BTW: can you please clarify, when using else with conditionals make sense, especially:
- conditionals for many certificates
- configuration for many virtual hosts

I did not share the whole configuration, because:
  • it is big: lighttpd -p ... results in more than 4000 lines
  • it contains too much private information
  • I did not find the time, to reduce it to a reasonable size for this issue

RE: Problem with conditionals and hostnames - Added by gstrauss about 2 months ago

Reverse the nested conditions solves the problem - thank you.

That suggests to me there was something else in your configuration that set alias.url which overrode the setting you shared. Reversing the conditionals probably put something at a different level and allowed same conditionals with identical scope to be merged.

BTW: can you please clarify, when using else with conditionals make sense, especially:
- conditionals for many certificates
- configuration for many virtual hosts

Use whatever works well for sane logical constructs.

For many certificates, and if your organizational structure allows, it can be is more efficient to have a wildcard certificates for *.example.org, if applicable, or to have a fewer number of certificates grouped by ownership and having multiple Subject Alternative Names (SAN) in the certificate, rather than a single domain per certificate.

lighttpd conditional matching is generally quite fast. Still, if you do have a separate certificate for each and every vhost, then for large configurations, you might notice a difference in performance if you help lighttpd optimize the tree of conditionals

$HTTP["host"] == "abc" {
    ssl.pemfile = "..." 
}
$HTTP["host"] == "def" {
    ssl.pemfile = "..." 
}
$HTTP["host"] == "ghi" {
    ssl.pemfile = "..." 
}

For the above, lighttpd will try to match all three conditionals for every TLS request.
That is less efficient than the following, which will stop at the first match.
$HTTP["host"] == "abc" {
    ssl.pemfile = "..." 
}
else $HTTP["host"] == "def" {
    ssl.pemfile = "..." 
}
else $HTTP["host"] == "ghi" {
    ssl.pemfile = "..." 
}

Another tip to reduce configuration processing is to put shared config into the global scope, and then to configure conditionals only for exceptions when there are only a few exceptions.

RE: Problem with conditionals and hostnames - Added by gstrauss about 2 months ago

More performance: instead of having an access log per vhost, consider having a single access log, and modifying the log format to include the vhost name. Then, log can be post-processed to split out the logs, e.g. after log rotation, or lighttpd mod_accesslog can be configured to log to a piped-logger which could split out the logs. Various choices are available depending on how and when your organization accesses and uses the logs.

RE: Problem with conditionals and hostnames - Added by gstrauss 27 days ago

Sometimes, it may be useful to set a default for the majority in the config, and then to undo that setting for the (few) exceptions.

# default: redirect http to https
$HTTP["scheme"] == "http" {
    url.redirect = ("" => "https://${url.authority}${url.path}${qsa}")
    url.redirect-code = 308
}

$HTTP["host"] == "exception1.example.org" {
    # undo the default redirect set by all previous conditions in the config file
    url.redirect = ()
}
$HTTP["host"] == "exception2.example.org" {
    # undo the default redirect set by all previous conditions in the config file and set different url.redirect conditions
    url.redirect = ("^/foo" => "/bar")
    url.redirect-code = 308
}

Note: the latter conditions above undo the default redirect set by all previous conditions in the config file unless there was another $HTTP["host"] "exception1.example.org" higher up in the same nesting scope and that condition was merged with the first occurrence $HTTP["host"] "exception1.example.org" in the same nesting scope.

    (1-6/6)