Кеширование 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
            Зачем сайт? Достаточно тут правильно расставлять теги
              +4
              он уже есть — sysoev.ru/nginx/docs/ :)
              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
                        О, спасибо, я как раз целый день ковырялся, почему никс сессии игнорит — скрипты упорно теряли сессии.

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

                      Самое читаемое