Реализация HTTPS для 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