1

nginx FAQ Is there a proper way to use nginx variables to make sections of the configuration shorter, using them as macros for making parts of configuration work as templates? ) saying (bold is mine):

Q: Is there a proper way to use nginx variables to make sections of the configuration shorter, using them as macros for making parts of configuration work as templates?

A: Variables should not be used as template macros. Variables are evaluated in the run-time during the processing of each request, so they are rather costly compared to plain static configuration. Using variables to store static strings is also a bad idea. Instead, a macro expansion and "include" directives should be used to generate configs more easily and it can be done with the external tools, e.g. sed + make or any other common template mechanism.

For example instead of one super long add_header Content-Security-Policy for better readability I am using:

set $CSP "default-src 'none'";

set $CSP "${CSP}; connect-src 'self'";

set $CSP "${CSP}; script-src 'self' https://*.domain.org 'unsafe-inline' 'unsafe-eval'";

set $CSP "${CSP}; style-src 'self' https://*.domain.org 'unsafe-inline'";

set $CSP "${CSP}; img-src 'self' data: https://*.domain.org";

set $CSP "${CSP}; font-src 'self' https://*.domain.org";

## CSP closing colon.
set $CSP "${CSP};";

add_header Content-Security-Policy "$CSP";

How much impact on nginx performance would that use of variable have appropriately? Has there been any performance testing / research on that subject?

adrelanos
  • 25
  • 6

2 Answers2

1

Variables are evaluated in the run-time during the processing of each request

Imagine each request you must get and save the variable from memory or run-time, how much time and memory you need to spend? Maybe if you have a few requests it's not significant, but if you have so many requests it's will very significant

It's just like an open static plain vs a dynamic file. There is nothing faster than the plain file being accessed directly.

In your case, you use a variable then set it so many times. I can't benchmark using Nginx it's self because of lack of infrastructure, but I want to share the benchmark of the PHP script that suite for an analogy to your case.

You want to set this text:

default-src 'none'; connect-src 'self; script-src 'self' https://.domain.org 'unsafe-inline' 'unsafe-eval'; style-src 'self' https://.domain.org 'unsafe-inline'; img-src 'self' data: https://.domain.org; font-src 'self' https://.domain.org;

with PHP you can set it using this code:

$csp = "default-src 'none'; connect-src 'self; script-src 'self' https://*.domain.org 'unsafe-inline' 'unsafe-eval'; style-src 'self' https://*.domain.org 'unsafe-inline'; img-src 'self' data: https://*.domain.org; font-src 'self' https://*.domain.org;";

But because of some reasons you want to set multiple times before set the real output. So the code will be like this:

$csp =  "default-src 'none'";
$csp = $csp."; connect-src 'self'";
$csp = $csp."; script-src 'self' https://*.domain.org 'unsafe-inline' 'unsafe-eval'";
$csp = $csp."; style-src 'self' https://*.domain.org 'unsafe-inline'";
$csp = $csp."; img-src 'self' data: https://*.domain.org; font-src 'self' https://*.domain.org";
$csp = $csp.";";

I run 1st code using a loop for 10 million times and takes 0.0725793838500982 seconds and 2nd code takes 1.049282026290894 seconds. So 2nd code takes about 15 times slower. And it just shows a request time benchmark, not memory, CPU, disk, or any other resource usage benchmark. maybe 2nd code will take much more resources than the first one too.

Like it happens in PHP, it also will happen to Nginx run-time too. So as on Nginx docs said Using variables to store static strings is costly and also a bad idea. If it's same as PHP analogy above the cost is 15 times slower

1

You should benchmark your configuration itself, because many factors from your particular setup affect the slowdown.

So, first set up configuration where desired CSP is set in one go.

Benchmark the configuration using for example Siege: https://github.com/JoeDog/siege

Then, set up configuration like in your question, and run the same benchmark test again.

This way you can get an accurate answer for your setup.

In any case, I recommend that you build your configuration file using a configuration management system like Ansible, Chef or Puppet. In the source, you can split the lines however you like, but the end result would be optimal for nginx.

Tero Kilkanen
  • 34,499
  • 3
  • 38
  • 58