Тщательно понять сопоставление местоположения Nginx

Nginx

Расположение Nginx реализует обработку запросов подразделениями, некоторые URI возвращают статический контент, некоторые распространяются на внутренние серверы и т. д. Сегодня давайте подробно разберемся с его правилами сопоставления.

Пример простейшего расположения выглядит следующим образом.

server {
    server_name website.com;
    location /admin/ {
    # The configuration you place here only applies to
    # http://website.com/admin/
    }
}

синтаксис, поддерживаемый местоположениемlocation [=|~|~*|^~|@] pattern { ... }, на первый взгляд довольно сложно, давайте рассмотрим его по порядку.

тип модификатора местоположения

Модификатор "=": требует, чтобы путь точно совпадал

server {
    server_name website.com;
    location = /abcd {
    […]
    }
}
  • http://website.com/abcdсовпадение
  • http://website.com/ABCDможет соответствовать, или может не совпадать, в зависимости от того, чувствительна ли файловая система операционной системы к регистру. ps: Mac по умолчанию нечувствителен к регистру, и при использовании git будут большие ямы.
  • http://website.com/abcd?param1&param2совпадение, игнорируя строку запроса
  • http://website.com/abcd/Несоответствие, с окончанием/
  • http://website.com/abcdeНесоответствие

Модификатор "~": обычное соответствие с учетом регистра

server {
    server_name website.com;
    location ~ ^/abcd$ {
    […]
    }
}

^/abcd$Это регулярное выражение означает, что строка должна начинаться с/начать с$конец, середина должна бытьabcd

  • http://website.com/abcdсовпадение(точное совпадение)
  • http://website.com/ABCDНесоответствие,Деликатный случай
  • http://website.com/abcd?param1&param2совпадение
  • http://website.com/abcd/Несоответствие, который не может соответствовать регулярному выражению
  • http://website.com/abcdeНесоответствие, который не может соответствовать регулярному выражению

"~*" регулярное сопоставление без учета регистра

server {
    server_name website.com;
    location ~* ^/abcd$ {
    […]
    }
}
  • http://website.com/abcdсовпадение(точное совпадение)
  • http://website.com/ABCDсовпадение(без учета регистра)
  • http://website.com/abcd?param1&param2совпадение
  • http://website.com/abcd/ 不匹配, который не может соответствовать регулярному выражению
  • http://website.com/abcde 不匹配, который не может соответствовать регулярному выражению

Модификатор ## "^~": сопоставление префикса Если расположение является наилучшим соответствием, то этот модификатор больше не выполняет проверку регулярных выражений для строк, соответствующих этому местоположению. Обратите внимание, что это не совпадение с регулярным выражением, оно должно иметь приоритет над совпадением с регулярным выражением.

Найти порядок и приоритет

При наличии нескольких правил локации у nginx более сложный набор правил, приоритеты следующие:

  • точное совпадение=
  • совпадение префикса^~(Немедленно прекратите последующие регулярные поиски)
  • Регулярное сопоставление по порядку в файлах~или~*
  • Соответствует префиксу, соответствует без каких-либо модификаций.

Общая идея этого правила такова.

Сначала точное совпадение, без поиска^~Если сопоставление префикса отсутствует, выполняется обычное сопоставление, и, наконец, возвращается результат сопоставления префикса (если есть).

Если приведенные выше правила непросты для понимания, вы можете увидеть следующий псевдокод (очень важно)

function match(uri):
  rv = NULL
  
  if uri in exact_match:
    return exact_match[uri]
  
  if uri in prefix_match:
    if prefix_match[uri] is '^~':
      return prefix_match[uri]
    else:
      rv = prefix_match[uri] // 注意这里没有 return,且这里是最长匹配
   
  if uri in regex_match:
    return regex_match[uri] // 按文件中顺序,找到即返回
  return rv

упрощенныйNode.jsНаписанный код выглядит следующим образом

function ngx_http_core_find_location(uri, static_locations, regex_locations, named_locations, track) {
  let rc = null;
  let l = ngx_http_find_static_location(uri, static_locations, track);
  if (l) {
    if (l.exact_match) {
      return l;
    }
    if (l.noregex) {
      return l;
    }
    rc = l;
  }
  if (regex_locations) {
    for (let i = 0 ; i < regex_locations.length; i ++) {
      if (track) track(regex_locations[i].id);
      let n = null;
      if (regex_locations[i].rcaseless) {
        n = uri.match(new RegExp(regex_locations[i].name));
      } else {
        n = uri.match(new RegExp(regex_locations[i].name), "i");
      }
      if (n) {
        return regex_locations[i];
      }
    }
  }

  return rc;
}

анализ случая

Дело 1

server {
    server_name website.com;
    location /doc {
        return 701; # 用这样的方式,可以方便的知道请求到了哪里
    }
    location ~* ^/document$ {
        return 702; # 用这样的方式,可以方便的知道请求到了哪里

    }
}

curl -I  website.com:8080/document
HTTP/1.1 702

Согласно приведенным выше правилам, второй будет иметь более высокий приоритет.

Случай 2

server {
    server_name website.com;
    location /document {
        return 701;
    }
    location ~* ^/document$ {
        return 702;
    }
}
curl -I  website.com:8080/document

Второе соответствует регулярному выражению и имеет приоритет над первым нормальным соответствием префикса.

Случай 3

server {
    server_name website.com;
    location ^~ /doc {
        return 701;
    }
    location ~* ^/document$ {
        return 702;
    }
}
curl http://website.com/document
HTTP/1.1 701

первое совпадение префикса^~После попадания не будет искать обычные совпадения, поэтому будет первым попаданием

Случай 4

server {
    server_name website.com;
    location /docu {
        return 701;
    }
    location /doc {
        return 702;
    }
}

curl -I website.com:8080/documentвозвращениеHTTP/1.1 701,

server {
    server_name website.com;
    location /doc {
        return 702;
    }
    location /docu {
        return 701;
    }
}

curl -I website.com:8080/documentвсе еще возвращаюсьHTTP/1.1 701

При сопоставлении префикса возвращается самое длинное совпадающее местоположение, независимо от порядка расположения.

Случай 5

server {
	listen 8080;
	server_name website.com;

    location ~ ^/doc[a-z]+ {
        return 701;
    }

    location ~ ^/docu[a-z]+ {
        return 702;
    }
}

curl -I website.com:8080/documentвозвращениеHTTP/1.1 701

изменить порядок

server {
	listen 8080;
	server_name website.com;

    location ~ ^/docu[a-z]+ {
        return 702;
    }
    
    location ~ ^/doc[a-z]+ {
        return 701;
    }
}

curl -I website.com:8080/documentвозвращениеHTTP/1.1 702

Обычное сопоставление заключается в использовании порядка в файле, поиске и возврате