Закрываем доступ к сайту с помощью .htaccess и PHP

Владимир | | htaccess, PHP, Web разработка.

deny access

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

Дело в том, что недавно я просматривал собственные посты и заметил, что в одном из них задача решается далеко не лучшим образом. Речь шла о блокировке доступа к блогу на движке WordPress.

Решение, которое я тогда предложил, работоспособное и им вполне можно пользоваться. Но только какое-то оно сложное 🙂

Нужно было использовать несколько файлов .htaccess для разных папок блога. Доступ для администратора получался немного ограниченным, например, статические ссылки не работали.

И, самое главное. Как абсолютно верно заметил Олег Лобач в комментариях к тому посту, если сайт закрыт на обслуживание, то необходимо отправлять заголовок с 503 кодом (Service Unavailable).

Для посетителей этот код роли не играет, они все равно увидят страницу с описанием ошибки, но вот поисковые боты – другое дело. Думаю, никому не захочется, чтобы в индекс поисковика попала фраза «Зайдите позже» вместо контента блога.

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

Нужно будет выполнить всего два шага.

1) Создать страницу, которую будем показывать посетителям во время обслуживания сайта.

2) Добавить пару строк в файл .htaccess, который находится в корне блога.

Кстати, этот метод подойдет практически для любого сайта не зависимо от используемого движка.

Идея заключается в ограничении доступа на основании IP адреса. Т.е. при попытке доступа к сайту web сервер (apache) проверит ваш IP и если он не совпадет с заданным, то все запросы будут перенаправлены на специальную страницу.

Итак, приступим.

Шаг 1. Создаем страницу с сообщением о том, что сайт временно закрыт.

Назовем её maintain.php и разместим в корне сайта.

<?php
header('HTTP/1.0 503 Service Unavailable');
header('Retry-After: 3600');
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Обслуживание сайта</title>
</head>
<body>
<h1>Извините, в данный момент сайт закрыт на обслуживание</h1>
<p>Зайдите немного позже</p>
</body>
</html>

Обсуждать тут особенно нечего. Саму страницу, конечно, лучше оформить в соответствии с общим дизайном сайта.

Но сейчас важнее заголовки (строки 2 и 3). Для их отправки используется стандартная функция PHP — header. В её первом параметре указываем заголовок.

Первый заголовок отправляет 503 HTTP код, а второй (Retry-After) – время, через которое рекомендуется обновить страницу. В данном примере я установил 1 час (3600 сек), но вы можете выбрать любой интервал времени по своему усмотрению.

Шаг 2. Определяем собственный IP.

Тут ситуация следующая. Если у вас статический IP адрес, то вы его знаете и определять ничего не надо 😉

Но большинство интернет провайдеров просит за него дополнительные деньги. И если вы эту услугу не заказывали, то при подключении IP адрес будет выдаваться вам каждый раз новый (на практике он меняется довольно редко).

Поэтому заходим на эту страницу и переписываем свой текущий IP-шник.

Шаг 3. Делаем резервную копию файла .htaccess.

Например, с названием .htaccess_main.

Шаг 4. Добавляем проверку IP.

В качестве примера, рассмотрим .htaccess, который часто используется в блогах на WordPress.

<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>

Примечание. Правила в этом файле подходят для любого движка, в котором запросы обрабатываются скриптом index.php. Например, для сайтов, использующих фреймворк CodeIgniter такой .htaccess тоже вполне подойдет.

Добавляем две дерективы после RewriteBase /

RewriteCond %{REMOTE_ADDR} !=127.0.0.2
RewriteRule ^.* maintain.php [L]

Т.е. в результате получится.

<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /

RewriteCond %{REMOTE_ADDR} !=127.0.0.1
RewriteRule ^.* maintain.php [L]

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>

Естественно, вместо 127.0.0.1 нужно указать свой реальный IP адрес.

Принцип работы предельно простой. В директиве RewriteCond (строка 5) мы сравниваем IP адрес посетителя с указанным. И если они не совпадают – отправляем посетителя на maintain.php (строка 6). Буква L в квадратных скобках указывает, что если это правило выполнилось, то обработку запроса нужно прекратить. Т.е. все последующие строки будут проигнорированы.

Если же вы сами заходите на сайт (IP-шники совпадают), то директива RewriteRule (в строке 6) пропускается и web сервер обрабатывает запрос как обычно.

Шаг 5. Восстанавливаем доступ к сайту.

Для этого просто восстанавливаем старый .htaccess файл из резервной копии.

Небольшое дополнение.

Если в процессе работы у вас изменится IP адрес, то вам нужно повторить шаг 2, создать .htaccess с новым IP и скопировать его в корень сайта по FTP.

Вот и все!

Все вопросы и замечания оставляйте в комментариях. Буду рад ответить или обсудить 🙂

До встречи!

Интересно почитать

Штукатурка кирпича по пеноблокам — достаточно сложная операция… но только не для профессионалов!
Узнать все объявления белоруссии вы сможете с помощью нашего сайта.

  • А можно просто поставить плагин

    • k0ev, конечно же можно. Но я думаю, что гораздо интереснее и полезнее всётаки знать как это работает.
      Отличный пост. Как раз ломал голову над подобной проблеммой. 🙂 Спасибо!

    • Честно говоря, я не уверен, что с этим плагином будет проще. Особенно, если вы захотите переделать оформление под свой блог.
      В принципе, ничего сложного, но придется покопаться в файлах плагина. Отдельную страничку быстрее сделаете.
      А переписывание файла htaccess по FTP занимает не больше времени чем вход в админку и активация плагина.

      Выбирать, конечно, вам, но вряд ли вы выиграете много времени за счет этого плагина 😉

      • Я полностью согласен с тем что знать как работает полезно и нужно.

        Но плагин и генерирует 503 ошибку, и позволяет админу управлять сайтом и иметь доступ к UI пока сайт закрыт. Ну и естественно позволяет редактировать страницу с сообщением под любой дизайн.

        Ну и в конце концов согласитесь что проще поставить галочку 😉

        • Согласен, галочку поставить проще 🙂

          Но есть момент, который я упустил. В инструкции по одному из обновлений (по моему это был переход с версий 2.3 на 2.5, не помню точно) рекомендовалось сначала удалить все старые файлы движка, а потом записать новые (при обычном обновлении файлы записываются поверху).
          Я не разбирался с принципом работы Maintenance Mode, но рассчитывать на работу плагина при таком обновлении я бы не рискнул 😉

  • А можно просто поставить плагин

    • k0ev, конечно же можно. Но я думаю, что гораздо интереснее и полезнее всётаки знать как это работает.
      Отличный пост. Как раз ломал голову над подобной проблеммой. 🙂 Спасибо!

    • Честно говоря, я не уверен, что с этим плагином будет проще. Особенно, если вы захотите переделать оформление под свой блог.
      В принципе, ничего сложного, но придется покопаться в файлах плагина. Отдельную страничку быстрее сделаете.
      А переписывание файла htaccess по FTP занимает не больше времени чем вход в админку и активация плагина.

      Выбирать, конечно, вам, но вряд ли вы выиграете много времени за счет этого плагина 😉

      • Я полностью согласен с тем что знать как работает полезно и нужно.

        Но плагин и генерирует 503 ошибку, и позволяет админу управлять сайтом и иметь доступ к UI пока сайт закрыт. Ну и естественно позволяет редактировать страницу с сообщением под любой дизайн.

        Ну и в конце концов согласитесь что проще поставить галочку 😉

        • Согласен, галочку поставить проще 🙂

          Но есть момент, который я упустил. В инструкции по одному из обновлений (по моему это был переход с версий 2.3 на 2.5, не помню точно) рекомендовалось сначала удалить все старые файлы движка, а потом записать новые (при обычном обновлении файлы записываются поверху).
          Я не разбирался с принципом работы Maintenance Mode, но рассчитывать на работу плагина при таком обновлении я бы не рискнул 😉

  • а можно что-то типа такого дописать:
    ### SERVER on maintenance ###
    RewriteCond %{DOCUMENT_ROOT}/maintenance.html -f
    RewriteRule ^.*$ /maintenance.html [L]

    и положить в корень файл !maintenance.html, а во время проведения работ переименовывать его в maintenance.html …

  • а можно что-то типа такого дописать:
    ### SERVER on maintenance ###
    RewriteCond %{DOCUMENT_ROOT}/maintenance.html -f
    RewriteRule ^.*$ /maintenance.html [L]

    и положить в корень файл !maintenance.html, а во время проведения работ переименовывать его в maintenance.html …

  • Seroga

    В код закралась опечатка: извените нужно через И.

  • Seroga

    В код закралась опечатка: извените нужно через И.

  • Хорошее решение.

  • Хорошее решение.

  • zavodptica

    Либо реализовать через куки («админские куки» ;), тогда решается вопрос с динамическим айпишником

    • С куками удобнее, но нужно следить чтобы их никто не увёл 🙂

    • Вот с куками интереснее. Может сделаете примерчик как отслеживать по кукам?

      • Нет, такой пример делать не буду. На мой взгляд, это не очень безопасно.

  • zavodptica

    Либо реализовать через куки («админские куки» ;), тогда решается вопрос с динамическим айпишником

    • С куками удобнее, но нужно следить чтобы их никто не увёл 🙂

    • Вот с куками интереснее. Может сделаете примерчик как отслеживать по кукам?

      • Нет, такой пример делать не буду. На мой взгляд, это не очень безопасно.

  • Морская Сова

    Какой бред! — только и всего, сказали веб-разработчики и дизайнеры, сидящие рядом в комнате, в ответ я тихо промолчал. Зачем вообще закрывать сайт, даже на время? Это пережитки прошлого века. Соответственно никаких отвертки и пассатижи на первой странице сайта — не нужны. Делайте всё так, чтобы сайт не закрывать и пользователи продолжали на него заходить. Благо на дворе 2010 год и все программные средства для этого имеются.

    • Дело не в программных средствах. Дело в собственных ошибках и недоработках, которые потом приходится исправлять, а для этого на время нужно ограничить доступ к сайту. Иногда нужно просто расширить функционал.

  • Морская Сова

    Какой бред! — только и всего, сказали веб-разработчики и дизайнеры, сидящие рядом в комнате, в ответ я тихо промолчал. Зачем вообще закрывать сайт, даже на время? Это пережитки прошлого века. Соответственно никаких отвертки и пассатижи на первой странице сайта — не нужны. Делайте всё так, чтобы сайт не закрывать и пользователи продолжали на него заходить. Благо на дворе 2010 год и все программные средства для этого имеются.

    • Дело не в программных средствах. Дело в собственных ошибках и недоработках, которые потом приходится исправлять, а для этого на время нужно ограничить доступ к сайту. Иногда нужно просто расширить функционал.

  • Андрей

    Пожалуйста, объясните лузеру, зачем нужны эти строки? Что если их вообще упустить, работать разве не будет?

    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule . /index.php [L]

    Просто пытаюсь разобраться детально, и тут застопорился.

    • Андрей

      Также, во других статьях на эту тему, встречал, вместо 2-х строчек, как у Вас в примере, четыре:
      header('HTTP/1.1 503 Service Temporarily Unavailable');
      header('Status: 503 Service Temporarily Unavailable');
      header('Retry-After: 3600');
      header('X-Powered-By:');

      Как же все-таки правильно? По поводу статьи, спасибо, весь интернет перерыл, пока нашел толковое обьяснение.

    • Правильно будет почитать документацию apache 😉 , а не надеяться на чьи-то решения (в том числе и мои :)).

      Условия
      RewriteCond %{REQUEST_FILENAME} !-f
      RewriteCond %{REQUEST_FILENAME} !-d
      означают, что правило
      RewriteRule . /index.php [L]
      не нужно применять если запрошенный файл (или папка) реально существуют на диске.
      Если их убрать то запросы вида
      site.domen/css/styles.css
      будут преобразованы в
      site.domen/index.php/css/styles.css
      Т.е. они нужны чтобы сервер правильно отдавал статические файлы (не вызывая index.php).

      P.S. Не поймите меня неправильно, то, что вы пытаетесь разобраться — очень хорошо. Просто mod_rewrite довольно сложный и, не читая официальную документацию, нормально работать вы не сможете.

    • Заголовок
      header('HTTP/1.1 503 Service Temporarily Unavailable');
      я отправил из скрипта maintain.php.

  • Андрей

    Пожалуйста, объясните лузеру, зачем нужны эти строки? Что если их вообще упустить, работать разве не будет?

    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule . /index.php [L]

    Просто пытаюсь разобраться детально, и тут застопорился.

    • Андрей

      Также, во других статьях на эту тему, встречал, вместо 2-х строчек, как у Вас в примере, четыре:
      header('HTTP/1.1 503 Service Temporarily Unavailable');
      header('Status: 503 Service Temporarily Unavailable');
      header('Retry-After: 3600');
      header('X-Powered-By:');

      Как же все-таки правильно? По поводу статьи, спасибо, весь интернет перерыл, пока нашел толковое обьяснение.

    • Правильно будет почитать документацию apache 😉 , а не надеяться на чьи-то решения (в том числе и мои :)).

      Условия
      RewriteCond %{REQUEST_FILENAME} !-f
      RewriteCond %{REQUEST_FILENAME} !-d
      означают, что правило
      RewriteRule . /index.php [L]
      не нужно применять если запрошенный файл (или папка) реально существуют на диске.
      Если их убрать то запросы вида
      site.domen/css/styles.css
      будут преобразованы в
      site.domen/index.php/css/styles.css
      Т.е. они нужны чтобы сервер правильно отдавал статические файлы (не вызывая index.php).

      P.S. Не поймите меня неправильно, то, что вы пытаетесь разобраться — очень хорошо. Просто mod_rewrite довольно сложный и, не читая официальную документацию, нормально работать вы не сможете.

    • Заголовок
      header('HTTP/1.1 503 Service Temporarily Unavailable');
      я отправил из скрипта maintain.php.

  • Андрей

    Спасибо большое за ответы.

  • Андрей

    Спасибо большое за ответы.

  • Николай

    А можно написать не один, а несколько IP-адресов, и как их перечислить ч/з зпт, тчк,… или как?

  • Нет, через запятую не пройдет, нужно использовать условие OR

    RewriteCond %{REMOTE_ADDR} !=127.0.0.1 [OR]

    RewriteCond %{REMOTE_ADDR} !=127.0.0.2 [OR]

    RewriteCond %{REMOTE_ADDR} !=127.0.0.2

  • Нет, через запятую не пройдет, нужно использовать условие OR

    RewriteCond %{REMOTE_ADDR} !=127.0.0.1 [OR]

    RewriteCond %{REMOTE_ADDR} !=127.0.0.2 [OR]

    RewriteCond %{REMOTE_ADDR} !=127.0.0.2

  • Andrey

    Статья очень полезная, и не только для wordpress-а!
    Только зачем дважды вызывать функцию header()?
    Можно ведь так
    header('Retry-After: 3600', true, 503);
    ещё можно на всякий случай добавить
    <meta name=»robots» content=»noindex» />
    Спасибо!

  • Вы, наверное, правы, с точки зрения сокращения кода.
    Но, на мой взгляд, вариант с двумя вызовами читается легче.

  • Op Rul

    подскажите нубу пожалуйсто как правильно сделать тоже но наоборот. запретить определенным ip адресам доступ к сайту.. заранее благодарен)

  • Добавить в .htaccess следующие правила

    order allow,deny
    deny from 192.168.44.201
    deny from 224.39.163.12
    deny from 172.16.7.92
    allow from all

  • Op Rul

    Спасибо большое, разобрался. Пока искал ответы по поисковикам наткнулся на такую чтуку — http://blognot.su/2008/08/ogranichenie-dostupa-k-saitu/ .. по функциям понравилась (после определенного кол-ва заходов банит ip) .. но не работает( .. а по прямому обращению к файлу пишет ошибку на 73 строке(

  • Я этим скриптом сам не пользовался, поэтому в чем проблема не знаю.
    Может автор что-нибудь подскажет?

  • Pingback: Как правильно закрыть сайт на время | Хисяметдинов Дамир | Балаково, создание сайтов, 927-621-3606()

  • Truepassword

    SyntaxHighlighter угандошил исходный код файла «maintain.php». Все HTTP т другие сокращения обёрнуты тегом acronym.

    • SyntaxHighlighter ни при чём. Это другой плагин — Acronym. Пока отключил.

  • Kolobokk

    Спасибо! Сработало. Заглушка работает, а в WP, при этом, работает админка и верхушка сайта. Так как я менял движок, то установил сразу две заглушки ни старый и на новый, чтобы при переходе ни один не допустил юзеров к базе данных.
    Перед этим пробовал использовать коды htaccess из других статей, но WordPress заглушить не смог. Может дело в php файле, код которого Вы даёте.
    Ещё раз спасибо!

    • maintain.php не влияет на доступ к сайту.
      Если Вы все делали по инструкции и указали Ваш реальный IP в .htaccess, то Вы сами должны заходить на все страницы сайта (не только в админку), а другие посетители увидят только maintain.php

  • У меня такой вопрос, на блогспоте как можно запретить доступ с определенного домена? в крайнем случае айпи? только если есть ответ,плиз как для мега чайника

    • Сразу предупреждаю, я не работал с блогспотом. Но всё упирается в то, поддерживает этот серсис htaccess или нет. Проверить очень просто.

      Создаёте файл «.htaccess» (точка в начале имени обязательна) и загружаете его в корень сайта (возможно этот файл там уже есть).

      Затем добавляете в этот файл строки

      order allow,deny
      deny from 123.45.6.7
      allow from all
      И проверяете, работает или нет 🙂

  • Игорь Гринев

    А если в заглушке есть ссылки на дополнительные страницы то сервер на них не пускает. А как сделать чтоб ссылки работали? Подскажите пожалуйста.

    • Нужно изменить правило
      RewriteRule ^.* maintain.php [L]

      Под регулярное выражение ^.* попадают все страницы, вам нужно изменить его так, чтобы для нужных ссылок оно не применялось.

  • Пипец. Переехал на выделенный сервер и теперь ни один из способов закрыть не помогает. Также как и другие вижу заглушку и все. Уже не знаю где копать…

    • У вас случайно не установлен nginx в качестве фронтенда перед apache? В этом случае apache будет получать запросы от nginx, а не от вас.

  • jbond007

    Мне понравилось тут в комментариях Визор писал, насчет переименования, по-моему супер вариант без всяких постоянных редактирований. Если ip = 192.168.1.121 или в корне нет файла unavailable.php, то отдаем
    index.php, в противном случае грузится заглушка unavailable.php. Вот так у меня заработало

    RewriteEngine On
    RewriteBase /

    RewriteCond %{REMOTE_ADDR} !=192.168.1.121
    RewriteCond %{DOCUMENT_ROOT}/unavailable.php -f
    RewriteRule ^.* unavailable.php [L]

    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule . /index.php [L]

    Теперь как бы только это аккуратно прикрутить к уже существующему .htaccess )) А то у меня там столько правил и редиректов что даже боюсь его трогать… ))

    • Если в htaccess есть редиректы, которые ведут не на index.php, то будет сложно. Но если index.php — единственная точка входа, то, по-идее, правило

      RewriteCond %{REQUEST_FILENAME} !-f
      RewriteCond %{REQUEST_FILENAME} !-d
      RewriteRule . /index.php [L]

      у вас уже есть, поэтому достаточно добавить

      RewriteCond %{REMOTE_ADDR} !=192.168.1.121
      RewriteCond %{DOCUMENT_ROOT}/unavailable.php -f
      RewriteRule ^.* unavailable.php [L]

  • Подскажите, пожалуйста! Как при такой конструкции прописать несколько разрешенных IP?
    RewriteEngine on
    RewriteCond %{REQUEST_URI} !/maintenance.html$
    RewriteCond %{REMOTE_ADDR} !^123.123.123.123
    RewriteRule $ /maintenance.html [R=302,L]

    • В выражении RewriteCond можно использовать регулярные выражения. Попробуйте написать что-то вроде
      RewriteCond %{REMOTE_ADDR} !^123.123.123.d{1,3}
      В результате под это выражение будут попадать IP из диапазона
      123.123.123.0…123.123.123.255