Score:1

Substituting same value multiple times with nginx sub_filter

in flag

Edit: Reproducible example: https://github.com/searchingforlife/nginx-substitution

I am trying to add/inject arbitrary number of scripts to an HTML file/output. They all filter on the same value </body> to inject themselves.

nginx version: nginx/1.16.1

Imagine we have two conf files like so (simplified):

# script_one.conf

sub_filter "</body>" "<script src='/one.js'></script></body>";
# script_two.conf

sub_filter "</body>" "<script src='/two.js'></script></body>";

And our main nginx conf file:

location /one {
  sub_filter_once off;
  include /script_one.conf;
  include /script_two.conf;
}

This however does not seem to work and nginx only adds the first script. If the filter value is different (say one filters on </head> and the other on </body>), then nginx injects both scripts.

Is there a way to get around this limitation and have nginx inject both scripts? I know I can create a new conf file that combines both scripts into one and include that but that just doesn't scale when you have 10+ scripts to inject that can be combined in various ways depending on the location.

Michael Hampton avatar
cz flag
As shown, it should work. Something else is probably going on. Please post the output of `nginx -T`.
searchingforlife avatar
in flag
@MichaelHampton Added repo to reproduce this behavior: https://github.com/searchingforlife/nginx-substitution
mforsetti avatar
tz flag
try using `"></body>"` on the second `sub_filter`. you already know what's on your previous `sub_filter`, so try avoid identical matches on it on your next `sub_filter`, though IMO, you can just inject one script, and dynamically load other scripts from said script, e.g. use `sub_filter` to inject a bootstrap script, and load subsequent JS files on it.
Score:0
us flag

This is speculation, I haven't verified it from nginx source code. I assume nginx stores the keys to replace in a hash structure, that is, replace x with y.

Now, when nginx reads the configuration file in order, it first adds a key </body> to the replace map, and then replaces it with the second one.

searchingforlife avatar
in flag
You're likely right. Instead, I thought about a different approach that seems to solve this issue where we only rewrite once. If anyone is interested, see the solution branch: https://github.com/searchingforlife/nginx-substitution/tree/solution
Score:0
in flag

I ended up thinking a different approach which only rewrites once. If anyone is interested, see the solution branch: https://github.com/searchingforlife/nginx-substitution/tree/solution

Basically, we create a variable that holds names of scripts to inject, and then inject them all at once. This way, we end up only rewriting once.

location ~ ^/ {
  set $js_args "one,two";
  include scripts.conf;
}
# scripts.conf

if ($js_args ~ "^$") {
  set $js_args '';
}

set $scripts "";

if ($js_args ~* "(,|^)one([,]|$)") {
  set $scripts "${scripts}<script src='/one.js'></script>";
}

if ($js_args ~* "(,|^)two([,]|$)") {
  set $scripts "${scripts}<script src='/two.js'></script>";
}

sub_filter "</body>" "${scripts}</body>";
mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.