Решил собирать на этой странице всяческие полезные настройки, с которыми пришлось повозиться.
Но, прежде чем приступить, хочу Вам предложить освежить знания о том, как nginx обрабатывает запрос:
Синтаксис:
location [ = | ~ | ~* | ^~ ] uri { ... }
location { } @ name { ... }| = | полное строковые совпадение, например = / исключительно для корня, и даже файлы в корне уже сюда не подходят (приоритет максимальный) |
| ^~ | требуется совпадение только начала строки, например ^~ /img/ считается не регулярным выражением (приоритет высокий) |
| ~* | регулярное выражение без учета регистра (приоритет средний) |
| ~ | регулярное выражение с учетом регистра (приоритет ниже среднего) |
| не указан | например "location / {" или "location /data/ {" (приоритет минимальный), при этом если у вас написано сразу два данных правила, то нет уверенности в том, что отработает вначале (/ или /data/) |
Примеры специально расположенные в обратном порядке приоритетов:
# Данная конфигурация сопоставляется с любым запросом, в котором есть /data/ например /foo/data/bar
location /data/ {
return 200 "data on $uri";
}
# Совпадает с любым запросом, начинающимся с /, однако после всех регулярных выражений.
location / {
return 200 "root is $document_root on $uri";
}
# Соответствует запросам /api и /api/ но если файл doc.txt не найден, то nginx сделает
# внутренний запрос словно клиент запросил /info.gif (см. ниже регулярку с gif)
location ~ ^/api(|/)$ {
try_files /doc.txt /info.gif;
}
# Совпадает с любым запросом, начинающимся с /img/ и затем поиск прекращается,
# в этом примере нет регулярных выражений.
location ^~ /img/ {
try_files $uri =404;
}
# Совпадает с любым запросом заканчивающимся на png, ico, gif, jpgили jpeg. Однако, все
# запросы /img/ будут обработаны в предыдущем location
location ~* \.(png|ico|gif|jpg|jpeg)$ {
try_files $uri =404;
}
# Если совпадает точно с запросом /
location = / {
index README.md;
}
# Произойдет поиск файла /my.js и если его нет, заново начнется поиск, словно клиент запросил файл /my.php
location = /my.js {
try_files $uri /my.php$is_args$args;
}Как проверить существование файла в директории и если файла нет - выдать 404
location ^~ /dir/ {
try_files $uri =404;
}Как проверить существование файла в директории и если файла нет - показать файл по умолчанию
location ^~ /dir/ {
try_files $uri /images/default.gif;
}*выше описанные способы можно совместить:
location ^~ /dir/ {
try_files $uri /images/default.gif =404;
}Проксируем запросы на site.ru
location ^~ /img/ {
resolver 8.8.8.8;
proxy_pass http://site.ru$request_uri;
# если site.ru ответит кодом ответа > 300, то что делать с таким ответом решит директива error_page
proxy_intercept_errors on;
# например: при 400 показать 200 https://nginx.org/ru/docs/http/ngx_http_core_module.html#error_page
error_page 404 =200 /empty.gif;
}или без dns-resolver:
location ^~ /img/ {
proxy_pass http://1.9.107.167:80;
proxy_redirect http://1.9.107.167:80/ /;
proxy_set_header Host $host;
}Проксируем запросы на site.ru
location ^~ /outer/office/users/ {
proxy_pass http://site.ru:80;
# подменяем заголовок 'Location' словно ответил текущий сайт (а не site.ru)
proxy_redirect default;
}Если site.ru заредирект на http://site.ru:8080/login, то proxy_redirect заменит его на http://yapro.com/login
Проксируем yapro.ru/backend/page -> http://127.0.0.1:80/page (обрезая начало URL /backend/):
location ^~ /backend/ {
# если не указать, то в $_SERVER['HTTP_HOST'] будет содержаться 'yapro.backend:8888'
proxy_set_header Host yapro.backend;
# т.к. в конце указал слэш, то запрос на yapro.front/backend/page будет перенаправлен на yapro.backend/page
proxy_pass http://127.0.0.1:80/;
}Проксируем yapro.ru/backend/page -> http://yapro.backend/backend/page :
location ^~ /backend/ {
proxy_set_header Host yapro.backend;
proxy_pass http://127.0.0.1:80/backend/;
}Проксируем yapro.ru/article/page -> http://yapro.backend/backend/article/page :
location ^~ /article/ {
proxy_set_header Host yapro.backend;
proxy_pass http://127.0.0.1:80/backend/article/;
}Проксируем запрос на yapro.ru/v1/chat/completions -> https://api.openai.com/v1/chat/completions
location = /v1/chat/completions {
proxy_pass https://api.openai.com;
proxy_pass_request_headers on;
proxy_ssl_name api.openai.com;
proxy_ssl_server_name on;
}Проверяем существование файла в директории, а и если файла нет - ищем на другом сайте, и если там нет - выдаем 404
location ^~ /dir/ {
try_files $uri @static_svr1;
}
location @static_svr1 {
resolver 8.8.8.8;
proxy_pass http://site.ru$uri;
}Как подгружать файлы определенной директории с директории другого проекта
location ^~ /dir/ {
root /var/www/yandex.ru/;
}* переопределять root параметр является единственным верным и рабочим способом и никакие try_file не помогут (и не нужны).
Как использовать динамичный роутинг с ограничением (например по расширению файла)
location ~* ^/mobile/(.+)\.(ettf|svg) {
try_files /$1.$2 @rewrite;
}Как вернуть код 500 с содержимым
location /500.html {
return 500 "Whoa! Internal Server Error";
}Как обработать GET-переменные (описание вариантов сравнения)
location = /my/page.html {
if ($query_string = "id=15&category=8") {
return 403;
}
}Как изменить количество секунд, которое будет ждать Nginx от PHP-fpm
location ^~ /my/page {
fastcgi_read_timeout 55s;
include php-fpm.conf;
}server {
listen 80;
server_name www.yapro.ru yapro.ru;
return 301 https://yapro.ru$request_uri;
}
server {
listen 443 ssl http2;
server_name yapro.ru;
# . . . SSL files paths and other code
return 301 https://yapro.ru$request_uri;
}
server {
listen 443 ssl http2;
server_name yapro.ru;
# . . . SSL files paths and other code
}Но, когда нужно добавить доп. правило, а не редиректить все сразу, то нужно так:
server {
listen 80;
server_name forum.yapro.ru;
# Если URI точно равен "/":
location = / {
return 301 https://yapro.ru/index.php;
}
# Для всех остальных запросов:
location / {
return 301 https://yapro.ru$request_uri;
}
}server {
server_name www.yapro.ru;
rewrite ^(.*) http://yapro.ru$1 permanent;
}Поручим обработку всех 404 статусов "named location"-у nginxerror404
server {
server_name 1.2.3.4;
listen 8000 default_server;
root /app/backend/public;
location ~* ^/files/(.+)\.(jpg|jpeg)$ {
root /app/backend/var;
try_files /$1.$2 =404;
}
error_page 404 @nginxerror404;
location @nginxerror404 {
default_type 'text/html; charset=UTF-8';
root /;
try_files /app/backend/templates/bundles/TwigBundle/Exception/error404.html.twig =404;
}
}Здесь очень важно указать default_type 'text/html; charset=UTF-8'; иначе будет возвращаться Content-Type: application/octet-stream и в браузере можно увидеть: ERR_INVALID_RESPONSE
Из интересного, что даже такой пример будет обработан @nginxerror404:
location ~ \.php$ {
return 404;
}А вот так можно сделать что-то, что должно быть всегда 404 (не советую так прятать backoffice, это лишь пример):
location ^~ /backoffice/ {
error_page 404 /index.html;
}п.с. не проверял, но говорят вот так можно обрабатывать все остальные ошибки:
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}Проверено: при следующей настройки:
server {
server_name _SERVER_NAME_;
listen 8000 default_server;
root /app/forum;
location ~* \.php$ {
fastcgi_param SCRIPT_FILENAME /app/forum/index.php;
fastcgi_pass 127.0.0.1:9000;
fastcgi_split_path_info ^(.+\.php)(/.*)$;
include fastcgi_params;
}
}при этом $ cat /etc/nginx/fastcgi_params:
fastcgi_param QUERY_STRING $query_string;
fastcgi_param REQUEST_METHOD $request_method;
fastcgi_param CONTENT_TYPE $content_type;
fastcgi_param CONTENT_LENGTH $content_length;
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
fastcgi_param REQUEST_URI $request_uri;
fastcgi_param DOCUMENT_URI $document_uri;
fastcgi_param DOCUMENT_ROOT $document_root;
fastcgi_param SERVER_PROTOCOL $server_protocol;
fastcgi_param REQUEST_SCHEME $scheme;
fastcgi_param HTTPS $https if_not_empty;
fastcgi_param GATEWAY_INTERFACE CGI/1.1;
fastcgi_param SERVER_SOFTWARE nginx/$nginx_version;
fastcgi_param REMOTE_ADDR $remote_addr;
fastcgi_param REMOTE_PORT $remote_port;
fastcgi_param SERVER_ADDR $server_addr;
fastcgi_param SERVER_PORT $server_port;
fastcgi_param SERVER_NAME $server_name;
# PHP only, required if PHP was built with --enable-force-cgi-redirect
fastcgi_param REDIRECT_STATUS 200;Использование try_files директивы для дополнительной проверки существования файла index.php:
location / {
try_files /index.php =404;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $request_filename;
fastcgi_pass unix://var/run/php/php7.3-fpm.sock;
}Пропустите проверку существования для повышения производительности, явно указав имя файла PHP-скрипта (если вы уверены, что index.phpфайл всегда существует):
location / {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root/index.php;
fastcgi_pass unix://var/run/php/php7.3-fpm.sock;
}Или даже явно указав полный путь к index.phpфайлу (таким образом rootдирективу можно вообще опустить):
location / {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME /full/path/to/index.php;
fastcgi_pass unix://var/run/php/php7.3-fpm.sock;
}Чтобы убедиться, что поддержка отладки сконфигурирована, необходимо в консоле выполнить команду:
nginx -V | grep "--with-debug"
если результат есть, значит nginx скомпилен с возможностью отладки и теперь для error-лога можно просто указать примечание debug:
server {
error_log /var/log/nginx/site.ru.error debug;
}Anti-hotlinking – защита файлов вашего сайта от прямого доступа с других сайтов или сервисов. По этому вопросу у меня отдельная статья »
Не проверялось: Autoindex
Autoindex — это функция, которая включает листинг директорий по http, средствами веб-сервера (конечно, если в директории нет настоящего index-файла).
location /testing {
autoindex on;
autoindex_exact_size off;
autoindex_localtime on;
}Не проверялось: как проксировать на нужный сервер, используя переменные из Cookies
Ситуация: приложение кладет в куку JSESSIONID значение k3t2, где 3 это номер сервера, 2 это порт 8020. Следовательно необходимо заворачивать клиентов по этим данным.
if ( $cookie_JSESSIONID ~ k(\d)t(\d)$ ) {
set $i $1;
set $p $2;
proxy_pass "http://10.0.0.${i}:80${p}0";
}*есть мнение, что в proxy_pass надо задавать url полностью. Т.е. нужно записать все в переменную и только потом proxy_pass http://$my_var
Не проверялось: под-директории и фронт-контроллеры в них
location /nested {
alias /var/www/nested/public;
try_files $uri $uri/ @nested;
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_param SCRIPT_FILENAME $request_filename;
fastcgi_pass unix:/var/run/php/php7.2-fpm.sock;
}
}
location @nested {
rewrite /nested/(.*)$ /nested/index.php?/$1 last;
}Однако вот пример работы вложенного location-a (проверено - работает).
Не проверялось: перенаправляем udp в http
server {
listen 80 udp;
proxy_pass http://elasticsearch:9200;
}или так
listen 80 udp;
location ^~ / {
proxy_pass http://elasticsearch:9200;
}или так
listen 80 udp;
location ^~ / {
# proxy_bind $remote_addr transparent;
# proxy_bind 1.0.0.0;
proxy_pass http://2.0.0.0:2555;
} server {
listen 80 default;
root /var/www/public;
error_log /var/log/nginx/error.log;
access_log /var/log/nginx/access.log;
location ~ ^/fpm_(status|ping)$ {
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_pass localhost:9000;
}
location ^~ /my/api {
fastcgi_pass localhost:9000;
# GLOBAL-переменные, будут доступны в PHP https://www.nginx.com/resources/wiki/start/topics/examples/phpfcgi/
fastcgi_param SCRIPT_FILENAME $document_root/index.php;
include fastcgi_params;
}
location / {
try_files $uri /index.php$is_args$args;
}
location ~ ^/index\.php(/|$) {
fastcgi_pass localhost:9000;
fastcgi_split_path_info ^(.+\.php)(/.*)$;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
fastcgi_param DOCUMENT_ROOT $realpath_root;
}Nginx имеет возможность декодировать URI, в реальном времени. Например, для того, чтобы найти соответствия “/app/%20/images” вы можете использовать “/app/ /images” для определения местоположения.
Для понимания ситуации, я использую nginx переменные:
Таким образом: $time_local - $request_time = дата времени начала запроса.
nginx.conf:
http {
log_format my_format '"$time_local - $request_time"';
access_log /var/log/nginx/access.log my_format;
server {
access_log /var/log/nginx/access.log my_format;
}
}Для этого в nginx есть (встроенный в бесплатную версию) модуль limit_req_zone, правда который показал себя странно при тестировании (или я его неправильно настроил). Я специально задрал значения в nginx-конфиге, сделал нагрузочный тест, но limit_req_zone отбросил тебе запросы, которые не должен был:
http {
limit_req_zone $request_uri zone=by_uri:10m rate=2000r/s;
server {
location / {
limit_req zone=by_uri burst=5 nodelay;Планирую этот модуль подвергнуть дальнейшему исследованию.
Официальный CORS-конфиг у меня работает, только если во все три места добавить:
add_header "Access-Control-Allow-Origin" $http_origin;
add_header 'Access-Control-Allow-Credentials' 'true';
плюс указав нужные HTTP-методы, например PUT:
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT';
Но как Вы заметили, в официальном конфиге используются if-ы, но if-ы это зло, и я решил в этом убедиться, написав:
set $baseCorsHeaders 0;
set $additionalCorsHeaders 0;
if ($request_method = 'OPTIONS') {
set $baseCorsHeaders 1;
}
if ($request_method = 'GET') {
set $baseCorsHeaders 1;
set $additionalCorsHeaders 1;
}
if ($request_method = 'POST') {
set $baseCorsHeaders 1;
set $additionalCorsHeaders 1;
}
if ($baseCorsHeaders = 1) {
add_header 'Access-Control-Allow-Origin' $http_origin always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range' always;
add_header 'Access-Control-Allow-Credentials' 'true';
}
# Wide-open CORS config for nginx - https://enable-cors.org/server_nginx.html
if ($request_method = 'OPTIONS') {
# Tell client that this pre-flight info is valid for 20 days
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain; charset=utf-8';
add_header 'Content-Length' 0;
add_header 'Access-Control-Allow-Credentials' 'true';
return 204;
}
if ($additionalCorsHeaders) {
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always;
}Как Вы наверное поняли, я убедился, что if-ы это зло (в примере выше отрабатывает только последний if).
CORS-вывод: nginx не очень удобный инструмент для написания конфигов с if-ами, к тому же если вы все же написали if, то конфиг может неожиданно переставать работать или работать неожиданно (что еще хуже).
Для этого в файле nginx.conf добавляем параметр server_tokens off;
server_tokens off;
server {
listen 80;
server_name site.com;Не проверял, но говорят надо еще объявлять:
# Hide headers that identify the server to prevent information leakage
proxy_hide_header X-Powered-By;
fastcgi_hide_header X-Powered-By;
server {
...api.openai.com (не проверено)server {
listen 80;
server_name openai-api.xxx.link;
error_log /var/log/nginx/openai-api/nginx-error.log;
access_log /var/log/nginx/openai-api/nginx-access.log main;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name openai-api.xxx.link;
error_log /var/log/nginx/openai-api/nginx-error-ssl.log;
access_log /var/log/nginx/openai-api/nginx-access-ssl.log main;
ssl_certificate /data/acme.sh/xxx.link/fullchain.cer;
ssl_certificate_key /data/acme.sh/xxx.link/bi83.link.key;
include /etc/nginx/ssl/options-ssl-nginx.conf;
ssl_dhparam /etc/nginx/ssl/ssl-dhparam.pem;
ignore_invalid_headers off;
client_max_body_size 0;
proxy_buffering off;
location /v1/audio/speech {
proxy_pass https://api.openai.com/v1/audio/speech;
proxy_ssl_name api.openai.com;
proxy_ssl_server_name on;
# Forward all headers
proxy_pass_request_headers on;
# proxy_http_version 1.1;
}
}Следующий код:
map $arg_one $var_two {
"one" "two";
}означает:
if ($arg_one = "one") {
set $var_two "two";
}Можно даже писать сразу несколько условий:
map $arg_one $var_two {
"one" "two";
"three" "four";
}что будет означать:
if ($arg_one = "one") {
set $var_two "two";
}
if ($arg_one = "three") {
set $var_two "four";
}А если переменная не совпала ни с одним значением, то можно указать значение по-умолчанию:
map $arg_one $var_two {
"one" "two";
"three" "four";
default "five";
} | Балансировщик | Веб-сервис |
| |
Нужно поправить:
Что отличается и это правильно:
Что это такое:
Если Вы используете докер-образ nginx (например nginx:1.24.0), то можете ENV-переменные. Для этого нужно:
RUN mkdir /etc/nginx/templates
COPY ./docker/nginx/default.conf.template /etc/nginx/templates/default.conf.templateТаким образом nginx при старте перенесет /etc/nginx/templates/default.conf.template в /etc/nginx/conf.d/default.conf
Пример шаблона (default.conf.template):
server {
server_name ${SERVER_NAME};
listen 80 default_server;
...
}Источники: 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 - 12 - 13 - 14 - 15