Nginx - can "if" be safely used at the level of a server block?
The Nginx docs warn in no uncertain terms that if is evil and should be avoided wherever possible, and there are similar warnings scattered across the length and breadth of the internet.
However, most of these warnings focus specifically on how badly if
behaves in location
blocks. Furthermore, the Nginx docs say that:
The only 100% safe things which may be done inside if in a location context are:
return ...;
rewrite ... last;
My question is: Is it safe to use an if
at the level of the server
block (rather than a location
block) if the only directive contained therein is a return
? For example, the following www-to-non-www redirect:
server {
listen 80;
server_name example.com www.example.com;
if ($http_host != example.com) {
return 301 http://example.com$request_uri;
}
# more config
}
Secondary question: I'm aware that the recommended method for doing this sort of thing is with two server
blocks that have different values for server_name
. If this is an acceptable use for if
, is there any reason to still use two separate server
blocks?
Solution 1:
One of the first steps when nginx receives a request is evaluating the HTTP Host
header / TLS SNI field, and selecting the virtual host based on the result of that evaluation.
This means that the Host
header is always processed.
Now, if you specify an if
statement for the redirection, it means that the Host
header will be checked twice, first to select the virtual host and then for checking the if
condition. That is twice the work for the processor.
A counter-argument could be the memory consumption for a separate server block. However, the memory allocation is the same for the lifetime of the nginx process, while the double-evaluation of Host
header happens on every request.
Solution 2:
From "If is Evil":
In some cases, it’s also possible to move ifs to server level (where it’s safe as only other rewrite module directives are allowed within it).
This suggests that if
in a server
block is safe(r) than in a location
block.