Can I use nginx environment variables within static files that nginx serves?

If I use a environment variable in the nginx config, and nginx is configured to serve only static files (html,js,css - e.g. a AngularJs app), is there any way I can use the environment variable within a JS file that nginx serves? Or is the only way to do this to run a non-static server, e.g. io.js, php, etc.


Btw you can't use environment variables natively in nginx's configuration.

When I talk about environment vars in the nginx config I mean like in this post: How can I use environment variables in Nginx.conf where they use env APP_WEB_1_PORT_5000_TCP_ADDR; and $ENV{"APP_WEB_1_PORT_5000_TCP_ADDR"};


Explain exactly your use case

My specific use case is that I have a nginx-powered AngularJS app in a docker container. The app is a "Single Page Application" that consumes a API running on another system altogether. Currently I run a different docker container between production and staging because the app has some config that is different, for example the Google-Analtyics code. This environment-specific data is kept in a config.js file and the values are currently hardcoded, one value for the master branch in git, and different value for the staging branch. I want to change the design so I can use the same container for both production and staging. I want to pass a ENV var into the container when I run it (docker run -e GACODE=UA-12345-6 ...) and have nginx use the ENV var (via env GACODE; and $ENV{"GACODE"} so the config.js file can use the GoogleAnaltyics codes that are passed in, rather than hardcoding them). I don't know whether this is possible or not (hence the question ;)). Using only nginx makes my container a single-process, whereas if I have to use io.js then I'll need multiple linked containers and more moving parts is more complex).


Solution 1:

I've been looking into resolving the same issue as the OP and this post came up in Google search, so thought I'd add a potential solution.

This post outlines how you can expose an environment variable within nginx config: https://blog.doismellburning.co.uk/environment-variables-in-nginx-config/

And you can return content without having a file in the file system: Reply with 200 from Nginx config without serving a file

Putting these two things together we end up with the following:

env MY_ENV_VAR;

# Snip

http {
    # Snip

    server {
        # Snip

        location ~ ^/config.js$ {
            add_header Content-Type text/javascript;
            set_by_lua $env_var 'return os.getenv("MY_ENV_VAR")';
            return 200 'const MY_ENV_VAR = \'$env_var\'';
        }
    }
}

This has worked for me in a test environment. It's a little cumbersome (not sure it's better than auto-generating a file on Docker launch that contains the vars you need).

Hope that helps.

Solution 2:

I experimented with using sub_filter and nginx environment variables but concluded that it isn't possible.

For example, this shows my experiments and shows using ENVs passed to nginx does not work within a server block:

env TOMTEST1; # OK - makes $ENV{"TOMTEST1"} available but NOT in server block.

http {

    server {

        # set $TOMTEST1 $ENV{"TOMTEST1"};    # KO - DOES NOT WORK - NGINX WONT START
        set $TOMTEST2   'tomtest2 Var';      # OK - THIS DOES WORK OK

        sub_filter 'TOM_TEST2' $TOMTEST2;       # OK - but not useful to me.
        sub_filter 'TOM_TEST3' 'tomtest3 Var';  # OK - but not useful to me.
        sub_filter_once off;                    # Don't stop at the first match, replace all of them
        sub_filter_types "text/javascript" "application/json"; # Apply to these mime types in addition to text/html

        # Serve static files
        location / {
           try_files $uri /index.html =404;
        }
        ...

where the static config.json file that I'm serving has various test strings like the following:

{
    "environment": "local",
    "test1": "$ENV{"TOMTEST1"}",
    "test3": "TOM_TEST2",
    "test4": "TOM_TEST3",
}

As @AD7six mentioned, the way to proceed is have a script that runs before nginx starts to produce a valid config file from placeholders. But this begs the question, if a script is going to run before nginx starts, I may as well set the contents of my config.json file in that script and not bother with sub_filter at all.

Solution 3:

sub_filter

If you want a simple string-replace, you can use sub_filter. For example:

server {
    sub_filter "REPLACE_THIS" "with this";
    sub_filter_once off; # Don't stop at the first match, replace all of them
    sub_filter_types "text/javascript" "application/json"; # Apply to these mime types in addition to text/html 

    # Everything else
}

However it's not possible to read an environment variable in nginx config - you can of course script writing the nginx config file any way you need/want to produce a valid config file and then reload nginx.