Python で https サーバーを動かす方法を紹介します。 正確には、自己署名証明書(いわゆる「オレオレ証明書」)を使った、開発用・確認用の簡易サーバーをローカルで動かす方法について紹介します。
この方法は本番環境・公開環境では使わないようにしてください。
まず最初に通常の http サーバーを動かす方法をおさらいします。
http サーバーを動かす
python -m http.server
Python で http サーバーを動かす最もかんたんな方法は、 Python 本体に同梱の http.server
モジュールを実行するやり方です。
python
コマンドの -m
オプションで http.server
モジュールを指定して実行します:
python -m http.server
デフォルトではポート 8000
が使用されます。
このコマンドを実行した後にブラウザで http://localhost:8000
にアクセスするとウェブサーバーが動いていることが確認できます。
引数でポート番号を指定することもできます。
# ポート 8001 を使用する
python -m http.server 8001
このコマンドを実行してブラウザから http://localhost:8001
にアクセスすると、次のようなログがターミナルに出力されます:
python -m http.server 8001
Serving HTTP on :: port 8001 (http://[::]:8001/) ...
::ffff:127.0.0.1 - - [07/Mar/2021 17:24:29] "GET / HTTP/1.1" 200 -
::ffff:127.0.0.1 - - [07/Mar/2021 17:24:29] "GET /favicon.ico HTTP/1.1" 404 -
その他利用可能なオプションについては --help
で確認することができます:
python -m http.server --help
usage: server.py [-h] [--cgi] [--bind ADDRESS]
[--directory DIRECTORY]
[port]
positional arguments:
port Specify alternate port [default: 8000]
optional arguments:
-h, --help show this help message and exit
--cgi Run as CGI Server
--bind ADDRESS, -b ADDRESS
Specify alternate bind address
[default: all interfaces]
--directory DIRECTORY, -d DIRECTORY
Specify alternative directory
[default:current directory]
Python の公式ドキュメントでも説明されているので興味のある方は読んでみてください。
socketserver.TCPServer
メッセージを出力するなどプラスアルファの処理をしたい場合は python -m http.server
は使えないので、 socketserver.TCPServer
を使う 10 行ほどのコードを書きます。
Python の公式ドキュメントには次のサンプルが載っています:
import http.server
import socketserver
PORT = 8000
Handler = http.server.SimpleHTTPRequestHandler
with socketserver.TCPServer(("", PORT), Handler) as httpd:
print("serving at port", PORT)
httpd.serve_forever()
このコードをファイルに保存して実行すれば、 python -m http.server
と同様に簡易の http サーバーが走らせられます。
http.server.HTTPServer
socketserver.TCPServer
の代わりに http.server.HTTPServer
を使うこともできます。
http.server.HTTPServer
は socketserver.TCPServer
を親に持つクラスです。
http.server.HTTPServer
を使えば socketserver
の import
はいらなくなるので、コードが少しだけシンプルになります:
import http.server
PORT = 8000
Handler = http.server.SimpleHTTPRequestHandler
with http.server.HTTPServer(("", PORT), Handler) as httpd:
print("serving at port", PORT)
httpd.serve_forever()
http.server.HTTPServer
のソースコードは GitHub で確認できます:
これでおさらいは終わりです。 ここから https サーバーの説明です。
https サーバーを動かす
SSL/TLS のある https サーバーを動かすには証明書が必要なので、まずは証明書を用意します。
私の手元の macOS に入っている LibreSSL では、コマンドひとつで自己署名証明書が生成できました:
openssl req -x509 -new -days 365 -nodes \
-keyout localhost.pem \
-out localhost.pem \
-subj "/CN=localhost"
コマンドが成功すると、次のようなテキストが出力されてカレントディレクトリに localhost.pem
ファイルが生成されます。
Generating a 2048 bit RSA private key
........+++
...............................+++
writing new private key to 'localhost.pem'
-----
他の OS を使っている場合は、その OS に合った方法で証明書を生成します。
つづいて、この証明書を使って https サーバーを動かします。 次のコードをファイルに保存します。
run_server.py
:
import ssl
from http.server import HTTPServer, SimpleHTTPRequestHandler
PORT = 443
CERTFILE = "./localhost.pem"
Handler = SimpleHTTPRequestHandler
context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
context.load_cert_chain(CERTFILE)
with HTTPServer(("", PORT), Handler) as httpd:
print("serving at address", httpd.server_address, "using cert file", CERTFILE)
httpd.socket = context.wrap_socket(httpd.socket, server_side=True)
httpd.serve_forever()
ここでは、 ssl.SSLContext
を使って証明書を読み込み HTTPServer
の ソケットを置き換えています。
上で作成した証明書 localhost.pem
と同じディレクトリでこのスクリプトを実行すれば https サーバーが起動します:
python run_server.py
serving at address ('0.0.0.0', 443) using cert file ./localhost.pem
その後ブラウザで https:/localhost
を開くとサーバーが動いていることが確認できるはずです(自己署名証明書を使っているため、最初ブラウザに警告 ↓ が出ます)。
ちなみに、 ssl.SSLContext
は使わず ssl.wrap_socket()
という関数を使うよりシンプルなやり方もありますが、その方法は Python 3.7 から deprecated となっているので、記事執筆時点では上のやり方をするのがよさそうです。
以上です。
ちなみに、 Python 本体に同梱の標準ライブラリだけを使う場合は上の方法がよいですが、そのような縛りが特に無い場合は便利なライブラリ( Gunicorn + Flask など)に頼った方がかんたんそうだと思います。
本記事のテーマに関連した Gist やリポジトリも作ったので興味のある方は参考にしてみてください:
- Sample: A simple https server with Python for development (Python 3.9+). · GitHub
- GitHub - gh640/python-https-servers