Using variables in Nginx location rules

前端 未结 4 502
自闭症患者
自闭症患者 2020-12-12 17:54

In Nginx, I\'m trying to define a variable which allows me to configure a sub-folder for all my location blocks. I did this:

set $folder \'/test\';

location         


        
相关标签:
4条回答
  • 2020-12-12 18:35

    This is many years late but since I found the solution I'll post it here. By using maps it is possible to do what was asked:

    map $http_host $variable_name {
        hostnames;
    
        default       /ap/;
        example.com   /api/;
        *.example.org /whatever/;
    }
    
    server {
        location $variable_name/test {
            proxy_pass $auth_proxy;
        }
    }
    

    If you need to share the same endpoint across multiple servers, you can also reduce the cost by simply defaulting the value:

    map "" $variable_name {
        default       /test/;
    }
    

    Map can be used to initialise a variable based on the content of a string and can be used inside http scope allowing variables to be global and sharable across servers.

    0 讨论(0)
  • 2020-12-12 18:36

    You can't. Nginx doesn't really support variables in config files, and its developers mock everyone who ask for this feature to be added:

    "[Variables] are rather costly compared to plain static configuration. [A] macro expansion and "include" directives should be used [with] e.g. sed + make or any other common template mechanism." http://nginx.org/en/docs/faq/variables_in_config.html

    You should either write or download a little tool that will allow you to generate config files from placeholder config files.

    Update The code below still works, but I've wrapped it all up into a small PHP program/library called Configurator also on Packagist, which allows easy generation of nginx/php-fpm etc config files, from templates and various forms of config data.

    e.g. my nginx source config file looks like this:

    location  / {
        try_files $uri /routing.php?$args;
        fastcgi_pass   unix:%phpfpm.socket%/php-fpm-www.sock;
        include       %mysite.root.directory%/conf/fastcgi.conf;
    }
    

    And then I have a config file with the variables defined:

    phpfpm.socket=/var/run/php-fpm.socket
    mysite.root.directory=/home/mysite
    

    And then I generate the actual config file using that. It looks like you're a Python guy, so a PHP based example may not help you, but for anyone else who does use PHP:

    <?php
    
    require_once('path.php');
    
    $filesToGenerate = array(
        'conf/nginx.conf' => 'autogen/nginx.conf',
        'conf/mysite.nginx.conf' => 'autogen/mysite.nginx.conf',
        'conf/mysite.php-fpm.conf' => 'autogen/mysite.php-fpm.conf',
        'conf/my.cnf' => 'autogen/my.cnf',
    );
    
    $environment = 'amazonec2';
    
    if ($argc >= 2){
        $environmentRequired = $argv[1];
    
        $allowedVars = array(
            'amazonec2',
            'macports',
        );
    
        if (in_array($environmentRequired, $allowedVars) == true){
            $environment = $environmentRequired;
        }
    }
    else{
        echo "Defaulting to [".$environment."] environment";
    }
    
    $config = getConfigForEnvironment($environment);
    
    foreach($filesToGenerate as $inputFilename => $outputFilename){
        generateConfigFile(PATH_TO_ROOT.$inputFilename, PATH_TO_ROOT.$outputFilename, $config);
    }
    
    
    function    getConfigForEnvironment($environment){
        $config = parse_ini_file(PATH_TO_ROOT."conf/deployConfig.ini", TRUE);
        $configWithMarkers = array();
        foreach($config[$environment] as $key => $value){
            $configWithMarkers['%'.$key.'%'] = $value;
        }
    
        return  $configWithMarkers;
    }
    
    
    function    generateConfigFile($inputFilename, $outputFilename, $config){
    
        $lines = file($inputFilename);
    
        if($lines === FALSE){
            echo "Failed to read [".$inputFilename."] for reading.";
            exit(-1);
        }
    
        $fileHandle = fopen($outputFilename, "w");
    
        if($fileHandle === FALSE){
            echo "Failed to read [".$outputFilename."] for writing.";
            exit(-1);
        }
    
        $search = array_keys($config);
        $replace = array_values($config);
    
        foreach($lines as $line){
            $line = str_replace($search, $replace, $line);
            fwrite($fileHandle, $line);
        }
    
        fclose($fileHandle);
    }
    
    ?>
    

    And then deployConfig.ini looks something like:

    [global]
    
    ;global variables go here.
    
    [amazonec2]
    nginx.log.directory = /var/log/nginx
    nginx.root.directory = /usr/share/nginx
    nginx.conf.directory = /etc/nginx
    nginx.run.directory  = /var/run
    nginx.user           = nginx
    
    [macports]
    nginx.log.directory = /opt/local/var/log/nginx
    nginx.root.directory = /opt/local/share/nginx
    nginx.conf.directory = /opt/local/etc/nginx
    nginx.run.directory  = /opt/local/var/run
    nginx.user           = _www
    
    0 讨论(0)
  • 2020-12-12 18:40

    You could do the opposite of what you proposed.

    location (/test)/ {
       set $folder $1;
    }
    
    location (/test_/something {
       set $folder $1;
    }
    
    0 讨论(0)
  • 2020-12-12 18:48

    A modified python version of @danack's PHP generate script. It generates all files & folders that live inside of build/ to the parent directory, replacing all {{placeholder}} matches. You need to cd into build/ before running the script.

    File structure

    build/
    -- (files/folders you want to generate)
    -- build.py
    
    sites-available/...
    sites-enabled/...
    nginx.conf
    ...
    

    build.py

    import os, re
    
    # Configurations
    target = os.path.join('.', '..')
    variables = {
      'placeholder': 'your replacement here'
    }
    
    
    # Loop files
    def loop(cb, subdir=''):
      dir = os.path.join('.', subdir);
    
      for name in os.listdir(dir):
        file = os.path.join(dir, name)
        newsubdir = os.path.join(subdir, name)
    
        if name == 'build.py': continue
        if os.path.isdir(file): loop(cb, newsubdir)
        else: cb(subdir, name)
    
    
    # Update file
    def replacer(subdir, name):
      dir  = os.path.join(target, subdir)
      file = os.path.join(dir, name)
      oldfile = os.path.join('.', subdir, name)
    
      with open(oldfile, "r") as fin:
        data = fin.read()
    
      for key, replacement in variables.iteritems():
        data = re.sub(r"{{\s*" + key + "\s*}}", replacement, data)
    
      if not os.path.exists(dir):
        os.makedirs(dir)
    
      with open(file, "w") as fout:
        fout.write(data)
    
    
    # Start variable replacements.
    loop(replacer)
    
    0 讨论(0)
提交回复
热议问题