Thank you for reading this post, don't forget to subscribe!
модуль ngx_http_proxy_module
Именно он реализует весь функционал, о котором пойдет речь. Допустим, у вас в локальной или виртуальной сети есть какие-то сервисы, не имеющие прямого доступа из интернета. А вы хотите таковой иметь. Можно пробрасывать нужные порты на шлюзе, можно что-то еще придумывать. А можно сделать проще всего — настроить единую точку входа на все свои сервисы в виде nginx сервера и с него проксировать различные запросы к нужным серверам.
Где он может использоваться:
- Допустим, у вас есть большой сервер с множеством контейнеров, например докера. На нем работает множество различных сервисов. Вы устанавливаете еще один контейнер с чистым nginx, на нем настраиваете проксирование запросов на эти контейнеры. Сами контейнеры мапите только к локальному интерфейсу сервера. Таким образом, они будут полностью закрыты извне, и при этом вы можете гибко управлять доступом.
- Допустим, у вас есть сервер с гипервизором proxmox или любым другим. Вы настраиваете на одной из виртуальных машин шлюз, создаете локальную сеть только из виртуальных машин без доступа в нее извне. Делаете в этой локальной сети для всех виртуальных машин шлюз по-умолчанию в виде вашей виртуальной машины со шлюзом. На виртуальных серверах в локальной сети размещаете различные сервисы и не заморачиваетесь с настройками фаервола на них. Вся их сеть все равно не доступна из интернета. А доступ к сервисам проксируете с помощью nginx, установленным на шлюз или на отдельной виртуальной машине с проброшенными на нее портами
- Допустим, у вас большой проект, разбитый на составные части, которые живут на разных серверах. К примеру, на отдельном сервере живет форум, по пути /forum от основного домена. Вы просто берете и настраиваете проксирование всех запросов по адресу /forum на отдельный сервер. Точно так же можно без проблем все картинки перенести на другой сервер и проксировать к ним запросы. То есть вы можете создать любой location и переадресовывать запросы к нему на другие сервера.
Настройка proxy_pass в nginx
Рассмотрим самый простой пример. Буду использовать свой технический домен test.ru в этом и последующих примерах. Допустим, у нас есть сайт blog.test.ru. В DNS создана A запись, указывающая на ip адрес сервера, где установлен nginx — nginx_srv. Мы будем проксировать все запросы с этого сервера на другой сервер в локальной сети blog_srv, где реально размещается сайт. Конфиг для секции server:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
server { listen 80; server_name blog.test.ru; access_log /var/log/nginx/blog.test.ru-access.log; error_log /var/log/nginx/blog.test.ru-error.log; location / { proxy_pass http://192.168.13.31; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Real-IP $remote_addr; } } |
Заходим по адресу http://blog.test.ru. Мы должны попасть на blog_srv, где тоже должен работать какой-то веб сервер. В моем случае это будет тоже nginx. У вас должно открыться содержимое, аналогичное тому, что вы увидите, набрав http://192.168.13.31 в локальной сети. Если что-то не работает, то проверьте сначала, что по адресу директивы proxy_pass у вас все корректно работает.
Посмотрим логи на обоих сервера. На nginx_srv вижу свой запрос:
1 2 3 |
77.37.224.139 - - [19/Jan/2018:15:15:40 +0300] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko" Проверяем blog_srv: |
94.142.141.246 - - [19/Jan/2018:15:15:40 +0300] "GET / HTTP/1.0" 304 0 "-" "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko" "77.37.224.139"
Как мы видим, запрос сначала пришел на nginx_srv, был переправлен на blog_srv, куда он пришел уже с адресом отправителя 94.142.141.246. Это адрес nginx_srv. Реальный же ip адрес клиента мы видим только в самом конце лога. Это неудобно, так как директива php REMOTE_ADDR не будет возвращать настоящий ip адрес клиента. А он очень часто бывает нужен. Мы это дальше исправим, а пока создадим в корне сайта на chat_srv тестовую страничку для проверки ip адреса клиента следующего содержания:
<?php
echo $_SERVER['REMOTE_ADDR']
?>
Назовем ее myip.php. Перейдем по адресу http://blog.test.ru/myip.php и проверим, как сервер определит наш адрес. Он покажет адрес nginx_srv. Исправляем это и учим nginx передавать реальный ip адрес клиента на сервер.
Передача реального ip (real ip) адреса клиента в nginx при proxy_pass
В предыдущем примере мы на самом деле передаем реальный ip адрес клиента с помощью директивы proxy_set_header, которая добавляет в заголовок X-Real-IP настоящий ip адрес клиента. Теперь нам нужно на принимающей стороне, то есть blog_srv сделать обратную замену — заменить информацию об адресе отправителя на ту, что указана в заголовке X-Real-IP. Добавдяем в секцию server следующие параметры
1 2 3 4 5 6 7 |
set_real_ip_from 94.142.141.246; real_ip_header X-Real-IP; Полностью секция server на blog_srv в самом простом варианте получается следующей |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
server { listen 80 default_server; server_name blog.test.ru; root /usr/share/nginx/html; <strong> set_real_ip_from 94.142.141.246; real_ip_header X-Real-IP;</strong> location / { index index.php index.html index.htm; try_files $uri $uri/ =404; } location ~ \.php$ { fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; fastcgi_intercept_errors on; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_ignore_client_abort off; } error_page 404 /404.html; location = /40x.html { } error_page 500 502 503 504 /50x.html; location = /50x.html { } } Сохраняем конфиг, перечитываем его и снова проверяем <em>http://blog.test.ru/myip.php</em>. Вы должны увидеть свой реальный ip адрес. Его же вы увидите в логе web сервера на blog_srv. |
Передача https через nginx с помощью proxy pass
Если у вас сайт работает по https, то достаточно настроить ssl только на nginx_srv, если вы не беспокоитесь за передачу информации от nginx_srv к blog_srv. Она может осуществляться по незащищенному протоколу. Рассмотрю пример с бесплатным сертификатом let’s encrypt. Это как раз один из кейсов, когда я использую proxy_pass. Очень удобно настроить на одном сервере автоматическое получение всех необходимых сертификатов. Сейчас будем считать, что у вас стоит certbot и все готово для нового сертификата, который потом будет автоматически обновляться.
Для этого нам надо на nginx_srv добавить еще один location — /.well-known/acme-challenge/.
Полная секция server нашего тестового сайта на момент получения сертификата будет выглядеть вот так:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
server { listen 80; server_name blog.zeroxzed.ru; access_log /var/log/nginx/blog.zeroxzed.ru-access.log; error_log /var/log/nginx/blog.zeroxzed.ru-error.log; location /.well-known/acme-challenge/ { root /web/sites/blog.zeroxzed.ru/www/; } location / { proxy_pass http://192.168.13.31; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Real-IP $remote_addr; } } |
Перечитывайте конфиг nginx и получайте сертификат. После этого конфиг меняется на следующий:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
server { listen 80; server_name blog.test.ru; access_log /var/log/nginx/blog.test.ru-access.log; error_log /var/log/nginx/blog.test.ru-error.log; return 301 https://$server_name$request_uri; # редирект обычных запросов на https } server { listen 443 ssl http2; server_name blog.test.ru; access_log /var/log/nginx/blog.test.ru-ssl-access.log; error_log /var/log/nginx/blog.test.ru-ssl-error.log; ssl on; ssl_certificate /etc/letsencrypt/live/blog.test.ru/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/blog.test.ru/privkey.pem; ssl_session_timeout 5m; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_dhparam /etc/ssl/certs/dhparam.pem; ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH'; ssl_prefer_server_ciphers on; ssl_session_cache shared:SSL:10m; location /.well-known/acme-challenge/ { root /web/sites/blog.test.ru/www/; } location / { proxy_pass http://192.168.13.31; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Real-IP $remote_addr; } } |
Проверяем, что получилось. Наш сайт работает по https, при том, что мы вообще не трогали сервер, где этот сайт располагается. Конкретно с web сайтом это может быть не так актуально, но если вы проксируете запросы не на обычный сайт, а на какой-то нестандартный сервис, который трудно перевести на https, это может быть хорошим решением.
Проксирование определенной директории или файлов
Рассмотрим еще один пример. Допустим, у вас форум живет в директории http://blog.test.ru/forum/, вы хотите вынести форум на отдельный web сервер для увеличения быстродействия. Для этого к предыдущему конфигу добавьте еще один location.
1 2 3 4 5 6 7 |
location /forum/ { proxy_pass http://192.168.13.31; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Real-IP $remote_addr; proxy_redirect default; } |
Еще одно популярное решение. Вы можете отдавать картинки с одного сервера, а все остальное с другого. В моем примере, картинки будут жить на том же сервере, где nginx, а остальной сайт на другом сервере. Тогда у нас должна быть примерно такая конфигурация локаций.
1 2 3 4 5 6 7 8 9 10 |
location / { proxy_pass http://192.168.13.31; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Real-IP $remote_addr; } location ~ \.(gif|jpg|png)$ { root /web/sites/blog.test.ru/www/images; } |
Чтобы все это работало корректно, необходимо, чтобы сам сайт умел правильно размещать свои изображения. Вариантов это организовать множество. Можно как на сервере монтировать сетевые паки различными способами, так и программистам изменять код для управления размещением изображений. В любом случае, это комплексный подход к работе с сайтом.
Существует очень много директив для управления прокси-соединениями. Все они описаны в соответствующей документации nginx.
Особое внимание следует уделить директивам кэширования proxy_cache, если в этом есть потребность. Можно существенно увеличить отклик веб сайта, если подходящим образом настроить отдачу кэша. Но это тонкий момент и нужно настраивать в каждом конкретном случае отдельно. Готовых рецептов тут не бывает.