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.pem

Code 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()

HTTP сервер на Python - comments

En
Https сервер python (python)