Реализация HTTPS для PHP приложений: от разработки до продакшена

Раздел: Администрирование PHP -> Веб-сервер

Основное решение: Apache + Let's Encrypt (Certbot) для продакшена

Как настроить HTTPS на сервере Apache с PHP, используя бесплатные сертификаты Let's Encrypt?

Для большинства production-серверов оптимальным является автоматическое получение сертификата через Certbot. Этот способ надёжен, поддерживает автоматическое обновление и широко документирован.


# Установка Certbot (на примере Ubuntu/Debian)
sudo apt update
sudo apt install certbot python3-certbot-apache

# Получение и автоматическая настройка SSL для домена example.com
sudo certbot --apache -d example.com -d www.example.com

После выполнения команды Certbot изменяет конфигурацию Apache, добавляя <VirtualHost *:443> с корректными путями к сертификатам. Также создаётся перенаправление с HTTP на HTTPS.

Типичная проблема: Ошибка certbot: command not found или Module 'ssl' already loaded. Решение: убедитесь, что установлены модули Apache mod_ssl и mod_rewrite. Выполните sudo a2enmod ssl rewrite и перезапустите Apache.

Как использовать встроенный сервер PHP для разработки с HTTPS?

Для локальной разработки часто требуется тестировать HTTPS. Встроенный сервер PHP (php -S) не поддерживает SSL напрямую, но можно обернуть его через reverse proxy с Nginx или использовать самоподписанный сертификат с помощью stunnel или socat.


# Генерация самоподписанного сертификата
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes -subj "/CN=localhost"

# Запуск PHP сервера на фоне (порт 8080)
php -S 0.0.0.0:8080 -t /var/www/html &

# Прокси через socat (порт 443 на 8080)
sudo socat OPENSSL-LISTEN:443,cert=cert.pem,key=key.pem,verify=0,fork TCP:localhost:8080

Теперь обращение по https://localhost будет направлено на PHP-сервер. В браузере появится предупреждение о самоподписанном сертификате - для разработки это приемлемо.

Типичная проблема: Браузер блокирует соединение из-за недоверенного сертификата. Решение: добавить сертификат в доверенные (на Windows - дважды кликнуть по .crt, Установить сертификат → Доверенные корневые центры). На macOS - открыть Keychain, добавить, пометить как доверенный.

Какие альтернативы существуют для сервера Nginx с PHP-FPM?

Nginx часто используется как frontend-сервер с передачей запросов PHP через FastCGI. Настройка HTTPS здесь аналогична Apache, но конфигурация отличается.


# Пример server block для HTTPS (файл /etc/nginx/sites-available/example.com)
server {
    listen 443 ssl http2;
    server_name example.com www.example.com;

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    # Рекомендуемые настройки безопасности
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:...';

    root /var/www/example.com;
    index index.php index.html;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }
}

# Перенаправление HTTP → HTTPS
server {
    listen 80;
    server_name example.com www.example.com;
    return 301 https://$server_name$request_uri;
}

Для автоматического получения сертификата используйте certbot --nginx.

Как реализовать HTTPS только для PHP-части через reverse proxy?

Иногда PHP-приложение запущено на внутреннем сервере без SSL, а терминация TLS выполняется на пограничном прокси (например, HAProxy, пограничный Nginx). Этот подход упрощает управление сертификатами и позволяет централизованно применять политики безопасности.


# Пример HAProxy frontend с SSL
frontend https_front
    bind *:443 ssl crt /etc/ssl/example.pem
    default_backend php_back

backend php_back
    server php1 127.0.0.1:8080 check

В PHP-коде необходимо отслеживать заголовок X-Forwarded-Proto, чтобы приложение знало о протоколе и не выдавало смешанный контент.


// В PHP реализация проверки HTTPS через заголовки прокси
function isHttps() {
    if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on') return true;
    if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') return true;
    return false;
}

Типичная проблема: При перенаправлении на HTTPS в PHP-коде (header('Location: https://...')) может возникнуть бесконечная петля, если прокси не передаёт правильные заголовки. Решение: настроить модуль mod_remoteip в Apache или real_ip в Nginx для доверенных прокси.

Расширенные примеры и случаи использования

Автоматическое обновление сертификатов с помощью cron

Пример

# Добавление задачи в crontab (проверка дважды в день)
0 0,12 * * * /usr/bin/certbot renew --quiet --post-hook "systemctl reload nginx"

Настройка SSL с поддержкой HTTP/2 на Nginx

Пример

server {
    listen 443 ssl http2;
    # ... остальные параметры
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 1d;
    ssl_session_tickets off;
    ssl_stapling on;
    ssl_stapling_verify on;
    resolver 8.8.8.8 8.8.4.4 valid=300s;
    resolver_timeout 5s;
}

Использование Let's Encrypt с DNS challenge (wildcard сертификаты)

Пример

# Для доменов *.example.com требуется dns-01 challenge
sudo certbot certonly --manual --preferred-challenges dns -d "*.example.com" -d example.com

После указания TXT-записи в DNS получите сертификат, который действует на все поддомены.

PHP-скрипт для проверки и отображения информации о сертификате

Пример

<?php
$sslInfo = [];
if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on') {
    $cert = $_SERVER['SSL_CLIENT_CERT'] ?? null;
    // Для Apache mod_ssl можно получить дополнительную информацию
    $sslInfo['protocol'] = $_SERVER['SSL_PROTOCOL'] ?? 'unknown';
    $sslInfo['cipher'] = $_SERVER['SSL_CIPHER'] ?? 'unknown';
    echo 'HTTPS active: ' . $sslInfo['protocol'] . ' ' . $sslInfo['cipher'];
} else {
    echo 'No HTTPS connection';
}
?>
HTTPS active: TLSv1.3 TLS_AES_256_GCM_SHA384

Отладка смешанного контента с помощью Content-Security-Policy

Пример

// В PHP-заголовках
header("Content-Security-Policy: default-src https: 'self'; upgrade-insecure-requests");

Этот заголовок предписывает браузеру автоматически заменять HTTP на HTTPS для всех ресурсов.

Настройка PHP-сервера с поддержкой SSL через stunnel (альтернатива socat)

Пример

# Файл конфигурации stunnel /etc/stunnel/stunnel.conf
[php_https]
accept = 443
connect = 127.0.0.1:8080
cert = /path/to/cert.pem
key = /path/to/key.pem
# Запуск stunnel
sudo stunnel /etc/stunnel/stunnel.conf
# PHP сервер запускается отдельно
php -S 127.0.0.1:8080 -t /var/www

Проверка доступности HTTPS через командную строку

Пример

# Использование openssl для ручной проверки
openssl s_client -connect example.com:443 -servername example.com
# Использование curl с выводом деталей сертификата
curl -vI https://example.com 2>&1 | grep -E "SSL|TLS|certificate"
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
* Server certificate:
*  subject: CN=example.com
*  start date: Jan  1 00:00:00 2025 GMT
*  expire date: Apr  1 00:00:00 2025 GMT

Настройка HTTPS на сервере PHP - comments

En
Php server https (php)