Кеширование FastCGI-запросов в nginx

    Доброе утро, Хабр!

    В данной статье я приведу пример конфигурации nginx для кеширования FastCGI-запросов. При желании его можно использовать его для защиты от хабраэффекта, частично от DDoS'а и, как вариант, для облегчения жизни сервера с высокой нагрузкой.

    В секции «http» объявляем кеш-зону fastcgi_cache, с хранением кеша в папке /tmp/nginx с 2 уровнями вложенности, с максимальным размером 256Мб и кешем ключей 16Мб (неиспользуемые более 1 дня объекты будут автоматически удаляться):
    fastcgi_cache_path /tmp/nginx/ levels=1:2 keys_zone=fastcgi_cache:16m max_size=256m inactive=1d;

    Далее, в секции «server» правим соответствующий location:
    location ~ \.php$ {
                # Стандартная конфигурация для php
                fastcgi_pass   unix:/tmp/php-fcgi.sock;
                fastcgi_index  index.php;
                fastcgi_param  SCRIPT_FILENAME  /usr/local/www/somedir/$fastcgi_script_name;
                include        fastcgi_params;
                fastcgi_param  DOCUMENT_ROOT /usr/local/www/somedir/;
                
                fastcgi_pass_header Cookie; # Необходимо для передачи cookie в соответствующие переменные, например cookie с именем phpsessid будет находится в переменной $cookie_phpsessid
    
                fastcgi_ignore_headers Cache-Control Expires Set-Cookie; # Игнорируем заголовки, относящиеся к кешированию, полученные от FastCGI-сервера
    
                fastcgi_cache_key "$server_addr:$server_port$request_uri|$cookie_phpsessid"; # Формируем уникальный ключ; в данном случае различаем пользователей с помощью $cookie_phpsessid
    
                fastcgi_cache fastcgi_cache; # Говорим о том, что использовать надо вышеобъявленную кеш-зону fastcgi_cache
    
                fastcgi_temp_path  /tmp/nginx/temp 1 2; # Указываем папку для хранения временных файлов
    
                fastcgi_cache_use_stale updating error timeout invalid_header http_500; # Используем вариант из кеша (даже если он устарел) в случае ошибки
    
                fastcgi_cache_valid 10s; # Время жизни кеша для ответов 200, 301 & 302
    
                #fastcgi_cache_valid any 10s; # Таким образом можно закешировать любые ответы
                }
    

    Конфиг опробован на nginx версии >= 0.7.60 (на => 0.8.1 должен работать, но не тестировался).

    Документация: Директивы модуля ngx_http_fastcgi_module, Changelog.

    upd: Если не игнорировать заголовки Cache-Control и Expires, nginx ведёт себя соответственно их содержанию (что, в принципе, логично).

    upd/28.01.2011: добавлен параметр Set-Cookie к fastcgi_ignore_headers (начиная с nginx/0.8.44)

    upd/07.02.2011: необходимо реализовать отделение поисковых краулеров, т.к. при игнорировании cookies могут выкинуть из выдачи
    Поделиться публикацией
    Похожие публикации
    Реклама помогает поддерживать и развивать наши сервисы

    Подробнее
    Реклама
    Комментарии 27
    • +4
      Наверное, стоит ещё исключить POST-запросы, заключив всё в условие
      if ($request_method = GET ) {

      }
      • 0
        Каким образом? У меня вылетает например:
        [emerg]: «fastcgi_index» directive is not allowed here in /usr/local/nginx/conf/nginx.conf:182
        • 0
          Эм… location { } внутри server { } быть должен, соблюдается? У меня такое работает точно.
          • 0
            Игорь, location-то внутри server разумеется, а вот куда воткнуть if ($request_method = GET )?
            • 0
              Внутрь location, в него загнать все про кеш, т.е.

              if (… ) {
              fastcgi_pass_header Cookie;

              }
              • 0
                Да вот нифига.
                [emerg]: «fastcgi_pass_header» directive is not allowed here in /usr/local/nginx/conf/nginx.conf:186
                • 0
                  Есть смысл попробовать через if () отправлять в именованый локейшн я думаю, позже попробую.
                  • 0
                    Есть, но это тебе полностью пост обрубит, чего я старался избежать
                    • 0
                      Как вариант — 2 условия, на POST и на GET, соответственно 2 локейшна.
          • +3
            Начиная с 0.7.48, по умолчанию кэшируются только GET и HEAD. Чтобы POST кэшировался нужно добавить
            fastcgi_cache_methods GET HEAD POST;
        • +1
          Спасибо!

          N.B. Сделайте уже кто-нибудь сайт «Секреты Nginx'а» =)
        • 0
          > fastcgi_ignore_headers Cache-Control Expires;

          а это зачем?
          • 0
            Это чтоб скрипт не считал себя умнее сервера и не мешал кешировать как ему нравится
            • 0
              Если не игнорировать эти заголовки, nginx ведёт себя согласно их содержанию.
              • 0
                т.е. он «заменяет» данные в кеше согласно этому заголовку?
                и если его не будет, то контент будет отдаваться из кеша вечно (если не сработает inactive=1d)?
                • 0
                  Смотря что указано в них:
                  • 0
                    Извиняюсь, не дописал.

                    Если в Expires указана дата в будущем, будет отдавать вариант из кеша вплоть до этой даты;
                    Если в прошлом — будет постоянно запрашивать с FastCGI сервера;

                    Аналогично и с Cache-Control, см. RFC 2616, section 14
                    • 0
                      Т.е. если мы их игнорируем, то всегда будет отдаваться из кеша?
                      • 0
                        Да, если в кеше есть копия и её срок годности согласно fastcgi_cache_valid не истёк.
              • 0
                Не плохо бы добавить как вообще не кешировать для некоторых юзеров, например залогиненных (можно по кукам определять)
                Я такое использовал когда еще не было встроенного кеширования (сам генерировал статику). Как сейчас такое сделать с внутренним кешем даже не смотрел.

                    set $django 1;
                    set $args_old "";
                    if ($is_args = "?") {
                        set $args_old ?$args;
                    }
                
                    location / {
                        if (-f $request_filename/index.html$args_old) {
                            set $django 0;
                        }
                        if ($http_cookie ~* "sessionid=([^;]+)(?:;|$)" ) {
                            set $django 1;
                        }
                        if ($request_method = POST) {
                            set $django 1;
                        }
                        if ($django = 0) {
                            rewrite (.*) $1/index.html$args_old break;
                        }
                        if ($django) {
                            fastcgi_pass unix:/www/myserver/server.sock;
                            break;
                        }
                        index       index.html;
                        access_log      /var/log/nginx/myserver.log main;
                    }
                
                • 0
                  Это очень старый вариант, когда еще кажется не было @backend
                • 0
                  Если с последними версиями nginx возникли проблемы с передачей cookies или авторизацией — решение:
                  fastcgi_pass_header «Set-Cookie»;
                  вместо
                  fastcgi_pass_header Cookie;
                  • 0
                    О, спасибо, я как раз целый день ковырялся, почему никс сессии игнорит — скрипты упорно теряли сессии.

                  Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.