Кеширование 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 могут выкинуть из выдачи
    Share post
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 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;
              0
              Спасибо!
              * Пора учить себя читать changelog'и
              * Мне все-таки удасться сегодня поспать! =)
                +1
                Спасибо, Игорь! Нужно действительно читать changelog, а то в документации про это не написано.
              +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
                            О, спасибо, я как раз целый день ковырялся, почему никс сессии игнорит — скрипты упорно теряли сессии.

                          Only users with full accounts can post comments. Log in, please.