Конфигурация nginx для работы с Node.js и PM2

При размещении на сервере нескольких виртуальных хостов с приложениями, написанными на Node.js чаще всего применяют nginx в качестве проксирующего веб-сервера. А для поддержания работоспособности node-приложений отлично подходит менеджер процессов PM2, разработанный для серверного JavaScript.

PM2 содержит большое количество полезных инструментов: перезапуск приложений, балансировщик нагрузки, логирование, watch-утилиту, монитор ресурсов, автоматический деплой и т.д.

При использовании фреймворка Express node-приложение легко работает со статикой, ее сжатием и доставкой клиенту.

В таких случаях nginx используют только для проксирования запросов. Однако веб-серверу необходимо позволить делать свою работу, разгрузив бэкенд от ненужных задач.

Рассмотрим конфигурацию виртуального хоста.

Сбор конфигурации

Полная конфигурация приводится в конце статьи.

Основа

Основа виртуального хоста

server {
        listen 80;
        server_name sitename.ru; # указать свой сайт
        
		location / {
                proxy_pass http://localhost:3000;
                proxy_redirect off;
        }
        
        # сюда дописываем конфиги
}

Логирование

PM2 позволяет собирать логи из stdout-потока бэкенда, поэтому access-log на стороне веб-сервера можно отключить, а error-log при этом лучше оставить, чтобы не пропускать ошибки 5XX.

access_log off;
error_log /var/log/nginx/sitename.ru.log error;

Заголовки

При логировании бэкенду необходимо знать IP-адрес клиента. Так как запросы он получает от локального веб-сервера, то адрес клиента будет 127.0.0.1.

Эта проблема решается путем добавления заголовков:

proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-NginX-Proxy true;

Не забудьте указать заголовок X-Real-IP в маске логгера как IP-адрес клиента.

Статика

При использовании nginx отдавать статику на стороне node.js бэкенда не оптимально. Веб-сервер справится с этим лучше и сэкономит время на проксировании запроса.

Также статику желательно кешировать для увеличения скорости загрузки сайта.

В итоге получаем следующие директивы:

# Указываем адрес папки со статикой как корня приложения
root /home/nodeuser/sitename.ru/public/;

# Кеширование по расширению файла
location ~* \.(jpg|jpeg|gif|png|ico|txt|woff|otf|eot|svg|ttf|html|xml|css|js)$ {
	expires 30d;
    error_page 404 @notfound;
}

location @notfound {
	proxy_pass http://localhost:3000$request_uri;
}

Обратите внимание на location-директиву @notfound. Запросы на ненайденные файлы уходят на бэкенд — это позволяет отображать 404-ю страницу, предусмотренную node-приложением.

Редирект с поддомена WWW

Редирект с www-поддомена также лучше производить на стороне nginx. Для этого напишем отдельную директиву server:

server {
         server_name "~^www\.(.*)$";
         return 301 $scheme://$1$request_uri;
}

Полная конфигурация

Ниже приведена полная конфигурация nginx для виртуального хоста с бэкендом на node.js.

server {
        listen 80;
        server_name sitename.ru;
		root /home/nodeuser/sitename.ru/public/;
        access_log off;
		error_log /var/log/nginx/sitename.ru.log error;

        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_set_header X-NginX-Proxy true;

        location ~* \.(jpg|jpeg|gif|png|ico|txt|woff|otf|eot|svg|ttf|html|xml|css|js)$ {
                expires 30d;
                error_page 404 @notfound;
        }

        location / {
                proxy_pass http://localhost:3000;
                proxy_redirect off;
        }

        location @notfound {
                proxy_pass http://localhost:3000$request_uri;
        }

}
server {
         server_name "~^www\.(.*)$";
         return 301 $scheme://$1$request_uri;
}

Тем, кто разворачивает на своем сервере блоговый движок Ghost, рекомендую статью «Тонкости настройки движка Ghost».