Оглавление

Настройка nginx

Про настройку можно найти много, но нет ничего лучше официальной документации. Здесь я, пожалуй приведу примеры распространённых патернов.

Дефолтный виртуальный сервер

Скорее всего, вы будете конфигурировать виртуальные сервера (даже, если он у вас один; хотя, лучше делать, как минимум два, см. ниже).

Существует масса ботов, ломалок и прочей вирусни, которая ходит не по доменным именам, а по IP. Если кто-то пришёл к вам без заголовка Host, значит он либо из прошлого тысячелетия, либо — это что-то зловредное и его можно смело отшить.

Для этого мы и сделаем дефолтный сервер, куда будут попадать все, кто идут к вам без заголовка Host.

server {
  # ключевая строчка
  server_name default_server;
  # пути к логам напишите свои
  access_log /log/default_server.access.log;
  error_log /log/default_server.error.log error;

  listen 80 default_server;
  # это только для ценителей :-)
  listen 443 default_server ssl;

  # мелочи по вкусу
  server_tokens off;
  charset utf-8;
  error_page 404 /error_404.html;
  error_page 500 /error_500.html;
  error_page 502 /error_502.html;
  error_page 503 /error_503.html;
  error_page 504 /error_504.html;
  index index.html;

  # отшиваем всех
  location / {
    return 403 "HTTP 1.0?";
  }
}

WWWizer

Многие web-сервера доступны сразу по нескольким именам и на всех именах контент одинаковый: http://www.ya.ru/, http://ya.ru/. Некоторые сервера отдают разный контент, в зависимости, от www: http://opennet.ru/ и http://www.opennet.ru/.

И то, и другое мне кажется неудобным. Первое может нарушить работу JavaScript и cookies из-за same origin policy. Второе может ввести в замешательство пользователя.

Мне более всего симпатичен подход Google, который никогда не использует «naked domains». Ни в каких сервисах.

У меня сделано так же. Если пользователь идёт на michurin.net, то его сразу перенаправляет на www.michurin.net. Это два разных сервера и первый занимается только перенаправлением.

Причём, если пользователь идёт на michurin.net/somepage.html, то его перенаправят на соответствующий адрес — www.michurin.net/somepage.html

Сделать это очень просто. Вот конфиг сервера:

server {
  server_name michurin.net;
  access_log /log/www.michurin.net.wwwizer.access.log;
  error_log /log/www.michurin.net.wwwizer.error.log error;
  server_tokens off;
  charset utf-8;
  listen 80;
  return 301 $scheme://www.michurin.net$request_uri;
}

(кстати, именно для команды return мне и нужен http_rewrite_module; если бы не она, его можно было бы тоже не включать в сборку)

Основной сервер

Строится всё по той же схеме. Но внутри секции server надо не забыть указать путь к файлам:

alias /www/htdocs;

И можно написать что-нибудь интересное и полезное.

Весёлые ответы хакерам.

У меня нет ни PHP, ни ASP, ни директорий .svn, за которыми всё время охотятся всеразличные боты. Чтобы логи не засорялись ответами 404, я отдаю им короткие документика, которые выплёвывает сам nginx даже не обращаясь к файловой системе:

location ~ /\. {
  types {} default_type  text/html;
  return 200 "<html><body><h1>_:_ Here you are!";
}
location ~* \.PHP$ {
  types {} default_type  text/html;
  return 200 "<html><body><h1>_:_ No PHP here";
}
location ~* \.ASP$ {
  types {} default_type  text/html;
  return 200 "<html><body><h1>_:_ No ASP here";
}

Эти три блока избавляют меня от 95% ошибок 404 в логах и мониторинге.

nginx и HTTP basic auth

Тоже всё очень просто:

location /private {
  auth_basic "closed site";
  auth_basic_user_file htpasswd;
}

Данные для htpasswd можно подготовить такой командой:

echo "username:`echo '*PASSWORD*' | openssl passwd -apr1 -stdin`" >htpasswd

В эту секцию location можно вкладывать и другие секции с дополнительными ограничениями и/или возможностями.

Немного мониторинга

В nginx есть возможность посмотреть его текущее состояние

location /stub {
  stub_status on;
  # можно отключить логи
  #   access_log off;
  # можно запретить доступ снаружи
  #   allow 127.0.0.1;
  #   deny all;
  # хотя, мне кажется разумней вынести эту
  # фунциональность в отдельный виртуальный
  # сервер, работающий на порту, который вообще
  # снаружи не виден
}

Ответ выглядит примерно так

Active connections: 3
server accepts handled requests
 13082 13082 14380
Reading: 0 Writing: 3 Waiting: 0

Его можно обработать такими не сложными скриптами и записать в RRD:

x=`wget -qO- http://localhost:81/stub | awk -f http.awk`
rrdtool update http.rrd "N:$x"

где http.awk:

BEGIN {
  ac="U"
  rd="U"
  wr="U"
  wa="U"
  acc="U"
  hnd="U"
  req="U"
}

NR == 1 { ac=$3 }
NR == 3 { acc=$1; hnd=$2; req=$3 }
NR == 4 { rd=$2; wr=$4; wa=$6 }

END {
  print wr":"rd":"wa":"ac":"hnd":"acc":"req
}

uWSGI с мультидоменной джангой

Это незамысловатая конфигурация для вызова uWSGI:

location /bin {
  uwsgi_param SCRIPT_URL      "just not empty";
  uwsgi_param QUERY_STRING    $query_string;
  uwsgi_param REQUEST_METHOD  $request_method;
  uwsgi_param CONTENT_TYPE    $content_type;
  uwsgi_param CONTENT_LENGTH  $content_length;
  uwsgi_param REQUEST_URI     $request_uri;
  uwsgi_param PATH_INFO       /michurin$document_uri;
  uwsgi_param DOCUMENT_ROOT   $document_root;
  uwsgi_param SERVER_PROTOCOL $server_protocol;
  uwsgi_param HTTPS           $https if_not_empty;
  uwsgi_param REMOTE_ADDR     $remote_addr;
  uwsgi_param REMOTE_PORT     $remote_port;
  uwsgi_param SERVER_PORT     $server_port;
  uwsgi_param SERVER_NAME     $server_name;
  uwsgi_param REMOTE_USER     $remote_user;
  uwsgi_param SERVER_SOFTWARE $nginx_version;
  uwsgi_pass 127.0.0.1:18009;
}

Единственное, что в ней сделано, это магия со SCRIPT_URL, который должен быть просто не пустой, и вторая магия с PATH_INFO.

Обратите внимание, когда запрашивается URL http://www.michurin.net/bin/ok.asm Django будет видеть /michurin/bin/ok.asm. Как этим пользоваться, я расскажу подробнее в отдельной заметке по Django. Но многие, я думаю, уже догадались.

Экзотическое проксирование

Иногда нужны кроссдоменные запросы. Я, например, сделал себе собственный RSS-reader (GoogleReader-то накрылся). Чтобы победить кроссдоменность, и не делать бесплатный прокси для всех на свете, я просто передаю требуемый URL в отдельном HTTP-заголовке.

Сейчас будет понятно. Вот конфигурация:

location /proxy/ {
  proxy_pass $http_x_prox;
}

Я делаю AJAX-запрос на /proxy/, передаю в заголовке X-Prox URL RSS фида, и, чудесным образом, получаю этот фид со своего домена.

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

(И конечно, все URLы и имена заголовков не настоящие. Не пытайтесь использовать меня, как бесплатный прокси.)