Simons Blog | Newest post | About / Imprint

nginx configuration is horrible

Posted on: 2026-01-30

So recently I was tasked with moving a webserver configuration from Apache to nginx. The reason for that is ... nginx is faster? I don't know if that's actually the case. The client wanted it. Benchmarks with Apache and nginx are pretty much head to head, if configured correctly. But I'm not engaging in some holy war about which webserver is better. We use both, they each have their own advantages regarding features and configurability. You know, using the right tool for the job. In this particular instance both tools seemed right.

While porting the configuration over from Apache, I wanted to clean things up a bit and make things easier to configure. At this point nginx showed its ugly side. The configuration syntax makes it seem like it's a programming language, but it's not. It has similarities, but I've since learned to ignore that bit in my brain that says: "Look, it has if's and variables with dollars, it's just like PHP!" and tell it: "No, it's still a configuration language. Not a programming language". Anyway, here are some oddities which I've come across:

There are no AND/OR operators

You have comparisons, but you can't combine them in any way. There is no || or && so you have to write multiple if-statements:

set $check "0";
if ($var = "foo") {
    set $check "1";
}
if ($var = "bar") {
    set $check "1";
}
if ($check = "1") {
    # do stuff if var is either "foo" or "bar"
}

How do we get an AND? One might think you could put one "if" in another "if", but:

You can't nest if-statements

Not possible. Nginx will complain about an "if" not being valid inside another "if". Here's a workaround:

set $check "0";
if ($var1 = "foo") {
    set $check "1";
}
if ($var2 = "bar") {
    set $check "1$check";
}
if ($check = "11") {
    # do stuff if var1 is "foo" AND var2 is "bar"
}

Yes. That's string concatenation... If no variable matches, $check is "0", if only $var1 matches its "1", if only $var2 matches its "10". If both match, it's "11". Stupid? Yes. But it's one of the recommended ways to do it. And just wait, it gets worse.

No else statements

There is no "else" for an "if". Instead you have to invert the condition.

You can't easily have a dollar sign in a string

Because a dollar sign is used for variable expansion. Saner languages like PHP have escaping like '\$', but there are many other ways it would be possible to implement this. Like '$$' for example. Or just putting it into a predefined variable like '$dollar'. But nginx has none of those. Instead you have to (ab)use a module which doesn't do variable expansion to get a variable to contain the dollar sign, like so:

geo $dollar {
    default "$";
}

How why couldn't that be standard? Anyway, put this into your "http" block. No, it can't be in a "server" or "location". Then you can access a dollar sign using the "$dollar" variable.

add_header Content-Type text/plain;
return 200 "The product costs 200$dollar";

"set" only works in certain blocks

You can use "set" inside a "server" or inside an "if" block. That's good. But it would be awesome if defaults could be set, for example inside the global scope or an "http" block - and then overwritten if needed. But nope, not possible. You also have to initialize a variable, if you don't do that, the server won't start.

Strings can't be used as Regex

Assume you have a regex in a string. You want to match something against that string. That doesn't work, it will just never match. At least with all the issues above, the configtest nginx -t will fail, but here it just silently doesn't work.

set $test_regex ".*bar";
set $str "candybar";

if($str ~ $test_regex) {
    # will never execute
}
if($str ~ ".*bar") {
    # will execute
}

Don't count on ChatGPT to help you

It will happily generate nested ifs, hallucinate escape sequences which don't exist, try to match against regexes in variables, etc. Basically all the things mentioned above.