Как запустить HTTPS-сервер на Python?

Ответ

Запуск HTTPS-сервера на Python требует наличия SSL/TLS сертификата и приватного ключа. Подходы различаются для разработки и production-среды.

1. Для разработки: встроенные модули

Можно использовать стандартные модули http.server и ssl. Это удобно для быстрого локального тестирования.

Шаг 1: Генерация самоподписанного сертификата Для локальной разработки можно создать сертификат с помощью OpenSSL. Браузер будет выдавать предупреждение о безопасности, так как сертификат не подписан доверенным центром.

# Создает ключ (server.key) и сертификат (server.pem) на 365 дней
openssl req -x509 -newkey rsa:4096 -nodes -out server.pem -keyout server.key -days 365

Шаг 2: Запуск сервера

from http.server import HTTPServer, SimpleHTTPRequestHandler
import ssl

# Адрес и порт сервера
server_address = ('localhost', 4443)

# Создание HTTP-сервера
httpd = HTTPServer(server_address, SimpleHTTPRequestHandler)

# Оборачивание сокета в SSL/TLS
httpd.socket = ssl.wrap_socket(
    httpd.socket,
    keyfile="server.key",  # Путь к приватному ключу
    certfile='server.pem', # Путь к сертификату
    server_side=True
)

print("Сервер запущен на https://localhost:4443")
httpd.serve_forever()

2. Для разработки: с веб-фреймворками

Фреймворки, такие как Flask или FastAPI (Uvicorn), также поддерживают запуск с SSL-контекстом, что удобно при разработке.

Пример с Flask:

from flask import Flask

app = Flask(__name__)

@app.route('/')
def home():
    return "HTTPS работает!"

if __name__ == '__main__':
    # Указываем пути к сертификату и ключу
    app.run(ssl_context=('server.pem', 'server.key'))

3. Рекомендация для Production

В production-среде SSL/TLS-терминацию почти всегда выполняет reverse-proxy сервер (Nginx, Caddy), а не Python-приложение.

Почему?

  • Производительность: Nginx оптимизирован для обработки SSL-шифрования.
  • Безопасность: Разделение ответственности, приватные ключи не хранятся в коде приложения.
  • Управление: Проще управлять сертификатами (например, автоматическое обновление с помощью Let's Encrypt / Certbot) и конфигурацией.

Приложение (Gunicorn/Uvicorn) в этом случае работает по обычному HTTP, а Nginx принимает HTTPS-трафик и проксирует его на приложение.

Ответ 18+ 🔞

А, слушай, вот эта тема с HTTPS на Python — это вообще отдельный цирк, ей-богу. Ну, типа, вроде бы просто, а потом начинаешь ковыряться и понимаешь, что тут, блядь, слоёный пирог из хуйни. Ладно, разберём по полочкам, чтобы не было мучительно больно.

Для начала, если ты просто локально потестить хочешь, типа "а сработает ли?", то можно на коленке собрать. Но браузер будет орать как резаный, потому что сертификат ты сам себе нарисовал, а не купил у дяди из доверенной конторы.

Шаг первый: нарисовать бумажку, что ты — это ты. Открываешь терминал и пишешь вот эту магическую хуйню. Она тебе создаст ключ (server.key) и сертификат (server.pem). На 365 дней хватит, потом опять рисовать.

openssl req -x509 -newkey rsa:4096 -nodes -out server.pem -keyout server.key -days 365

Тебе там ещё кучу вопросов задаст — город, организацию и прочую ерунду. Можешь везде Enter жать, похуй. Главное — файлы появятся.

Шаг второй: запустить сервер, который будет притворяться серьёзным. Берёшь стандартный http.server, оборачиваешь его в SSL-конверт — и вуаля, ты король локального HTTPS.

from http.server import HTTPServer, SimpleHTTPRequestHandler
import ssl

server_address = ('localhost', 4443) # Слушаем тут
httpd = HTTPServer(server_address, SimpleHTTPRequestHandler)

# А вот тут магия: натягиваем SSL-презерватив на наш сервер
httpd.socket = ssl.wrap_socket(
    httpd.socket,
    keyfile="server.key",  # Вот твой ключ
    certfile='server.pem', # А вот твоя бумажка
    server_side=True
)

print("Сервер запущен на https://localhost:4443")
httpd.serve_forever() # И поехали!

Заходишь в браузер на https://localhost:4443, он тебе скажет "ОПАСНОСТЬ, БЛЯДЬ!", но ты смело жмёшь "Всё равно продолжить" и радуешься зелёному замочку. Ну, не совсем зелёному, скорее красному с черепом.


А если ты, допустим, на Flask или FastAPI пишешь, то там вообще красота. Всё уже придумано за тебя.

Вот смотри, Flask:

from flask import Flask
app = Flask(__name__)

@app.route('/')
def home():
    return "HTTPS работает, ёпта!"

if __name__ == '__main__':
    # Просто указываешь пути к тем же файлам — и всё, пиздец, ты в дамках.
    app.run(ssl_context=('server.pem', 'server.key'))

Запустил — и сиди, проверяй. Удобно, быстро, для разработки — то, что надо.


НО! Это всё, блядь, для песочницы. Для игрушек. Как только речь заходит про продакшн, про реальных пользователей — тут надо включать голову, а не просто скрипты копипастить.

Правильный путь для продакшена — это поставить перед своим Python-приложением нормальный, взрослый веб-сервер. Nginx, Caddy, Apache — не суть.

Почему, спросишь? А потому что:

  1. Производительность, ёбта! Nginx написан на C, он эту криптографическую хуйню разжёвывает в сотни раз быстрее, чем твой Python. Пусть он этим и занимается.
  2. Безопасность, ёпёрный театр! У тебя приватный ключ будет лежать не в коде приложения, куда любой левый процесс может залезть, а в отдельном, хорошо защищённом месте, куда имеет доступ только Nginx. Разделение обязанностей — наше всё.
  3. Удобство, ядрёна вошь! Хочешь автоматически обновлять сертификаты от Let's Encrypt? Для Nginx есть Certbot, который всё сделает за тебя, пока ты спишь. А твоё приложение вообще об этом не узнает — оно как работало по HTTP, так и работает.

Твоя архитектура тогда выглядит так: Пользователь (HTTPS) <-> Nginx (расшифровывает) <-> Твоё приложение на Gunicorn/Uvicorn (простой HTTP)

Приложение вообще не парится про SSL. Оно просто получает запросы от Nginx и отдаёт ответы. Всё. Красота.

Так что запомни: для теста — можешь накрутить SSL прямо в коде. Для боевого режима — отдай эту почетную обязанность Nginx и спи спокойно. Не изобретай велосипед, его уже до тебя изобрели, причём с мотором и подогревом сидений.