Общий хостинг DreamHost: как он устроен

Эта страница описывает аккаунт общего хостинга DreamHost так, как его видит владелец: логин dh_user, сервер <server>.dreamhost.com, корень документов ~/example.com/ и сайт example.com. Отличие от Beget не в бренде, а в веб-слое, и следующие главы показывают, где это задевает. Инструменты, которые понадобятся: curl, SSH, grep.

Только Apache, TLS в Apache

Спереди нет nginx. Apache принимает соединение и для адреса https сам держит сертификат. Заголовок ответа говорит только об Apache:

curl -sS -D - -o /dev/null https://example.com/

HTTP/2 200 content-type: text/html; charset=UTF-8 server: Apache

Зонд из проверки сервера даёт подробности. Запишите его в корень документов под именем, которое никто не угадает:

vi ~/example.com/probe-7f3a.php

<?php echo "HTTPS=", var_export($_SERVER['HTTPS'] ?? null, true), " XFP=", var_export($_SERVER['HTTP_X_FORWARDED_PROTO'] ?? null, true), " SERVER_SOFTWARE=", $_SERVER['SERVER_SOFTWARE'] ?? '', " sapi=", PHP_SAPI, "\n";

curl -sS https://example.com/probe-7f3a.php

HTTPS='on' XFP=NULL SERVER_SOFTWARE=Apache sapi=cgi-fcgi

HTTPS='on' означает, что TLS заканчивается в этом Apache. Никакой прокси не присылает X-Forwarded-Proto, поэтому XFP=NULL. Редирект на https, который проверяет только этот заголовок, срабатывает и на https-запросах и отправляет каждый адрес на самого себя. Правило, которое здесь работает, проверяет %{HTTPS}:

RewriteCond %{HTTPS} off RewriteCond %{HTTP:X-Forwarded-Proto} !https RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]

Цикл, который исправило это правило, вместе с командами, которые его показывают, — в статье Переезд на DreamHost: цикл. Удалите зонд, когда закончите:

rm ~/example.com/probe-7f3a.php

Статические файлы проходят через .htaccess

Переднего сервера нет, поэтому картинки, CSS и JavaScript тоже отдаёт Apache, и каждое правило в .htaccess срабатывает и для них. На Beget наоборот: существующий на диске статический файл отдаёт nginx, и Apache не читает для него правила (nginx перед Apache). На DreamHost из этого следуют две вещи:

Реальный случай. Сайт хранит свои PHP-классы, а с ними картинки и скрипты, которые они используют, в папке с именем .php. Его .htaccess отправляет PHP-страницу с PATH_INFO (page.php/extra) на чистый адрес:

RewriteCond %{REQUEST_FILENAME} -f RewriteCond %{REQUEST_URI} ^(.+\.php)/.+$ RewriteRule ^ https://%{HTTP_HOST}%1 [R=301,L]

Шаблон подходит и к /.php/img/...: имя папки кончается на .php, и за ним идёт слеш. На DreamHost картинка из этой папки отвечает 301, и редирект ведёт на папку:

curl -sS -o /dev/null -D - https://example.com/.php/img/cards/airport.jpg | grep -i -E '^(HTTP|location|server)'

HTTP/2 301 location: https://example.com/.php server: Apache

На странице видна сломанная картинка, а журнал ошибок ничего не объясняет. Какие файлы попали под правило, показывает журнал доступа, скрипты тоже:

awk '$9 == 301 && $7 ~ /\/\.php\// {print $7}' ~/logs/example.com/https/access.log | sort | uniq -c

3 /ru/.php/img/cards/actress-800x600.jpg 1 /.php/scripts/top10_script3.js 1 /.php/scripts/top10_script2.js 1 /ao/.php/scripts/top10_script2.js

Исправление: требовать перед .php имя файла:

RewriteCond %{REQUEST_FILENAME} -f RewriteCond %{REQUEST_URI} ^(.*[^/]\.php)/.+$ RewriteRule ^ https://%{HTTP_HOST}%1 [R=301,L]

[^/] означает любой символ, кроме слеша. Поэтому page.php/extra по-прежнему подходит, а /.php/img/cards/airport.jpg уже нет. Тот же запрос к серверу, похожему на Beget, и к серверу, похожему на DreamHost, до исправления и после, разобран в проверке 6 плана проверок Docker.

PHP как FastCGI

DreamHost запускает PHP как приложение FastCGI на всех тарифах хостинга (PHP overview). На общем сервере это значит, что mod_fcgid запускает обработчик на каждый сайт; файл обработчика назван по версии PHP, например php85.cgi для PHP 8.5. Зонд выше ответил sapi=cgi-fcgi, и две команды показывают то же самое из оболочки:

pgrep -l -u user php

2745746 php85.cgi

ls /usr/sbin/apache2*

/usr/sbin/apache2 /usr/sbin/apache2ctl

Apache установлен, но в списке его модулей нет php_module: PHP не внутри него. Полная проверка, включая apache2ctl -M на сервере, которым вы управляете, — в статье Какой у вас сервер.

Почему php_flag отвечает 500

php_flag и php_value — директивы mod_php. На DreamHost mod_php не загружен, поэтому Apache не знает этих слов. Одна такая строка в .htaccess останавливает каждый запрос в этой папке и ниже. Сайт отвечает 500 на каждой странице, включая главную:

curl -sS -D - -o /dev/null https://example.com/

HTTP/2 500 content-length: 533 content-type: text/html; charset=iso-8859-1 server: Apache

Журнал ошибок называет файл и первую неизвестную директиву:

tail -n 1 ~/logs/example.com/https/error.log

[Fri Nov 06 23:29:54 2026] [example.com] [alert] [client 203.0.113.7:37118] [pid 3064457] config.c(2045): /home/user/example.com/.htaccess: Invalid command 'php_flag', perhaps misspelled or defined by a module not included in the server configuration

Строки, которые это вызвали, с того же аккаунта:

grep -n -E '^\s*php_(flag|value)' ~/example.com/.htaccess

78:php_flag display_startup_errors off 79:php_flag display_errors off 80:php_flag html_errors off 81:php_flag log_errors on 82:php_flag ignore_repeated_errors off 83:php_flag ignore_repeated_source off 84:php_flag report_memleaks on 85:php_flag track_errors on 86:php_value docref_root 0 87:php_value docref_ext 0 91:php_value error_reporting -1 92:php_value log_errors_max_len 0

Закройте блок так, чтобы хостинг без mod_php его пропускал, а не падал:

vi ~/example.com/.htaccess

<IfModule mod_php.c> php_flag display_errors off php_flag log_errors on # the other ten lines stay inside this block </IfModule>

Полный блок из двенадцати строк и копия для отката — в статье 500 после переезда: исправление. Общий вид защитного блока — в статье IfModule.

Настройки PHP в phprc

Когда блок пропущен, PHP работает с настройками хостинга по умолчанию. DreamHost читает дополнительные настройки для каждой версии PHP из файла в домашней папке с именем phprc, который работает как php.ini (php.ini overview). Для PHP 8.5 путь — ~/.php/8.5/phprc:

vi ~/.php/8.5/phprc

display_startup_errors = Off display_errors = Off html_errors = Off log_errors = On ignore_repeated_errors = Off ignore_repeated_source = Off report_memleaks = On error_reporting = -1

track_errors убрали в PHP 8.0, а log_errors_max_len в PHP 8.1, поэтому их здесь нет. PHP читает файл при старте своего обработчика. Завершите работающий обработчик, чтобы следующий запрос запустил новый:

pkill -x php85.cgi

Провайдер даёт ту же инструкцию с killall -9 php85.cgi -u dh_user и объясняет, почему файл может выглядеть неработающим (My phprc file isn't updating). Зонд после перезагрузки показывает новые значения; полный вывод до и после — в статье случай с php_flag.

Журналы ошибок и доступа

Журналы лежат в домашней папке, по папке на домен и по папке на протокол. Для https-сайта из этого примера:

~/logs/example.com/https/error.log
~/logs/example.com/https/access.log

У http-сайта та же пара в ~/logs/example.com/http/. Apache пишет туда свои ошибки, а сообщения PHP следуют за ними, когда error_log пуст, с пометкой mod_fcgid: stderr:

[Sat Nov 07 01:01:52 2026] [example.com] [warn] [client 203.0.113.9:61816] [pid 1836802] fcgid_bucket.c(153): mod_fcgid: stderr: PHP Deprecated: Creation of dynamic property Related::$urls is deprecated in /home/user/example.com/.php/Related.php on line 10

Провайдер документирует те же файлы и просмотр в панели (Viewing and examining your error log via SSH). 500 из-за сломанной директивы — это строка config.c, как выше; 403 из-за прав — строка Permission denied.

SSH и права файлов

SSH включён, но пользователь сайта должен быть пользователем оболочки (SSH overview). Войдите с ключом, который загрузили, затем перейдите в корень документов:

ssh -i ~/.ssh/<key> dh_user@<server>.dreamhost.com

cd ~/example.com

Аккаунт работает с обычными правами UNIX: папки 755, файлы 644. Apache работает под другим пользователем, поэтому папка 700 или файл 600 для него невидимы и страница отвечает 403, или 404, когда закрыта вся папка. Провайдер описывает биты в статье Unix permissions overview. Если запустить команду из корня документов после загрузки, владелец — dh_user, а права такие:

stat -c '%a %U:%G %n' dev/python dev/python/files

755 dh_user:pg000000 dev/python 700 dh_user:pg000000 dev/python/files

rsync и zip сохраняют эти права, и новый сервер копирует их точно так же. Сохраните старые права в файл, затем исправьте папки и файлы на сервере и в локальной копии:

find ~/example.com -printf '%m %p\n' > ~/backups/example.com.modes.$(date +%Y%m%d-%H%M%S)

find ~/example.com -type d -exec chmod 755 {} +

find ~/example.com -type f -exec chmod 644 {} +

Следующая загрузка не должна вернуть права обратно. Копируйте с rsync --chmod=D755,F644; эта опция объяснена в статье Копирование файлов. Переезд, на котором возникла эта проблема, описан в примере, а Docker-образ воспроизводит её с корнем документов только для чтения, принадлежащим другому пользователю: проверка 5.

Python и Node.js

DreamHost документирует Python как скрипты CGI и FastCGI на общем хостинге. Файл Python должен заканчиваться на .py, .cgi или .fcgi, начинаться с пути интерпретатора, иметь права 755 и использовать переводы строк UNIX (Guidelines for setting up a Python file). Python 3 установлен; своя версия и пакеты ставятся в виртуальное окружение в домашней папке (Python overview).

Passenger на DreamHost не среда запуска: страница провайдера Supported and unsupported technologies перечисляет Passenger как NO, не поддерживается на серверах DreamHost. Поэтому у приложения на WSGI-фреймворке здесь нет точки входа Passenger, в отличие от Beget.

Node.js доступен на Managed VPS и выделенных серверах, но не на общем хостинге, как описывают та же страница поддерживаемых технологий и обзор Node.js overview.

Проверка на сервере, похожем на DreamHost

В репозитории есть Docker-образ, который копирует этот веб-слой: только Apache, PHP как FastCGI через mod_fcgid, без mod_php, папка phprc на каждый сайт, журналы в /logs/example.com/ и самоподписанный сертификат. Запустите на нём свой сайт до переезда: Образ, похожий на DreamHost.

Автор статьи: Arthur Isaev