HTTP и HTTPS серверы на Python: варианты реализации и примеры
Создание HTTP и HTTPS серверов на Python
Python предоставляет несколько способов запустить HTTP сервер, от простого встроенного модуля до мощных фреймворков. В этой статье рассматриваются различные подходы с акцентом на HTTPS, а также типичные ошибки и их решения.
Как создать простой HTTPS сервер, используя только стандартную библиотеку Python?
Наиболее быстрое решение для отладки или локального тестирования - модуль http.server в сочетании с ssl. Для этого потребуется файл сертификата и ключа (cert.pem и key.pem).
# server.py
import http.server
import ssl
server_address = ('', 443)
httpd = http.server.HTTPServer(server_address, http.server.SimpleHTTPRequestHandler)
# Создание SSL контекста
context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
context.load_cert_chain(certfile='cert.pem', keyfile='key.pem')
# Оборачивание сокета
httpd.socket = context.wrap_socket(httpd.socket, server_side=True)
print('Сервер запущен на https://localhost:443')
httpd.serve_forever()
Https сервер python (http сервер на python)
Для генерации самоподписанного сертификата используется команда:
openssl req -newkey rsa:2048 -nodes -keyout key.pem -x509 -days 365 -out cert.pemCode server python (сервер кода python (code-server))
Проблемы и решения:
- Ошибка ssl.SSLError: [SSL: CERTIFICATE_VERIFY_FAILED] - при использовании самоподписанного сертификата клиенты (браузеры) будут выдавать предупреждение. Для локальной разработки это допустимо, для продакшена нужен сертификат от центра сертификации.
- Порт 443 требует прав администратора на Linux/macOS. Альтернатива - использовать порт 8443 или 4443.
- Модуль http.server не предназначен для высокой нагрузки, так как однопоточный.
Цель использования: быстрое тестирование HTTPS, раздача статических файлов по защищённому каналу.
Вариант 1: HTTPS на Flask для веб-приложений
Как запустить локальный HTTPS сервер на Flask с SSL?
Flask является популярным микрофреймворком. Для HTTPS достаточно передать пути к сертификату и ключу в метод run().
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return 'Hello, HTTPS world!'
if __name__ == '__main__':
app.run(ssl_context=('cert.pem', 'key.pem'), host='0.0.0.0', port=5000)
локальный сервер python (локальный сервер на python)
Сертификаты создаются так же, как в базовом варианте.
Проблемы:
- Встроенный сервер Flask не подходит для продакшена из-за однопоточности.
- При использовании ssl_context='adhoc' Flask генерирует временный самоподписанный сертификат, но браузеры его не примут.
Цель: быстрая разработка и тестирование веб-приложения с HTTPS.
Вариант 2: HTTPS на FastAPI с Uvicorn
Как организовать защищённое API с помощью FastAPI и Uvicorn?
FastAPI использует ASGI сервер Uvicorn, поддерживающий SSL через флаги командной строки.
# main.py
from fastapi import FastAPI
app = FastAPI()
@app.get('/')
async def root():
return {'message': 'Hello FastAPI HTTPS'}
# командная строка
uvicorn main:app --host 0.0.0.0 --port 8000 --ssl-certfile cert.pem --ssl-keyfile key.pem
Для продакшена рекомендуется использовать обратный прокси (nginx) с SSL, а Uvicorn запускать без SSL.
Проблемы:
- При использовании самоподписанного сертификата клиенты (например, curl с флагом -k) будут работать, но браузеры покажут предупреждение.
- Если ключ защищён паролем, Uvicorn может не принять его - требуется ключ без пароля.
Цель: создание асинхронного API с HTTPS для микросервисов.
Вариант 3: Асинхронный HTTPS сервер на aiohttp
Как построить высокопроизводительный HTTPS сервер с aiohttp?
aiohttp - асинхронный фреймворк, позволяет запускать SSL через контекст.
from aiohttp import web
import ssl
async def handle(request):
return web.Response(text='Hello aiohttp HTTPS')
app = web.Application()
app.router.add_get('/', handle)
if __name__ == '__main__':
context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
context.load_cert_chain('cert.pem', 'key.pem')
web.run_app(app, host='0.0.0.0', port=8080, ssl_context=context)
Проблемы:
- aiohttp требует Python 3.7+ и установки зависимостей (pip install aiohttp).
- При большом количестве одновременных подключений нужно настраивать пулы соединений.
Цель: создание асинхронных серверов с высокой пропускной способностью, например, для WebSocket или Streaming.
Расширенные примеры работы с HTTPS серверами на Python
Генерация самоподписанного сертификата с указанием альтернативных имён (SAN)
Для корректной работы в современных браузерах требуется указать Subject Alternative Names. Команда OpenSSL:
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes -subj '/CN=localhost' -addext 'subjectAltName = DNS:localhost, IP:127.0.0.1'
Результат: файлы cert.pem и key.pem.
Проверка HTTPS сервера с помощью curl
Команда для тестирования с игнорированием проверки сертификата:
curl -k https://localhost:5000
Hello, HTTPS world!
Использование сертификатов Let's Encrypt (через Certbot)
Для реального домена можно получить бесплатный сертификат. После установки Certbot и выполнения:
sudo certbot certonly --standalone -d example.com
Сертификаты будут сохранены в /etc/letsencrypt/live/example.com/. Затем в Python коде указываются пути:
context.load_cert_chain(
'/etc/letsencrypt/live/example.com/fullchain.pem',
'/etc/letsencrypt/live/example.com/privkey.pem'
)
Запуск HTTPS сервера в Docker с самоподписанным сертификатом
Структура проекта:
# Dockerfile
FROM python:3.10-slim
WORKDIR /app
COPY server.py .
COPY cert.pem .
COPY key.pem .
EXPOSE 443
CMD ["python", "server.py"]
Сборка и запуск:
docker build -t https-python .
docker run -p 443:443 https-python
Результат: на хост-машине доступен https://localhost:443.
HTTPS сервер с поддержкой HTTP/2 (через aiohttp и h2)
Требуется установка pip install aiohttp[h2]. Пример:
from aiohttp import web
import ssl
async def handler(request):
return web.Response(text='HTTP/2 работает')
app = web.Application()
app.router.add_get('/', handler)
if __name__ == '__main__':
context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
context.load_cert_chain('cert.pem', 'key.pem')
web.run_app(app, host='0.0.0.0', port=8443, ssl_context=context, handle_signals=True)
Проверка: curl --http2 -k https://localhost:8443.
Обработка ошибок сертификата в клиентской программе на Python
Пример клиента, который доверяет самоподписанному сертификату через фиксу:
import urllib.request
import ssl
# Создание контекста без проверки сертификата
context = ssl.create_default_context()
context.check_hostname = False
context.verify_mode = ssl.CERT_NONE
response = urllib.request.urlopen('https://localhost:5000', context=context)
print(response.read().decode())
Hello, HTTPS world!
HTTPS сервер с автоматическим перенаправлением с HTTP на HTTPS
Два порта в одном скрипте (http.server). Пример с использованием threading:
import http.server
import ssl
import threading
class RedirectHandler(http.server.BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(301)
self.send_header('Location', f'https://{self.hostname}{self.path}')
self.end_headers()
def run_http():
server = http.server.HTTPServer(('', 80), RedirectHandler)
server.serve_forever()
def run_https():
server = http.server.HTTPServer(('', 443), http.server.SimpleHTTPRequestHandler)
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
ctx.load_cert_chain('cert.pem', 'key.pem')
server.socket = ctx.wrap_socket(server.socket, server_side=True)
server.serve_forever()
if __name__ == '__main__':
threading.Thread(target=run_http, daemon=True).start()
run_https()