Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
車輪の再発明をしよう!PHP で実装して学ぶ、Web サーバーの仕組みと HTTP の正体
Search
Sponsored
·
Ship Features Fearlessly
Turn features on and off without deploys. Used by thousands of Ruby developers.
→
H1R0
March 20, 2026
Programming
840
3
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
車輪の再発明をしよう!PHP で実装して学ぶ、Web サーバーの仕組みと HTTP の正体
H1R0
March 20, 2026
More Decks by H1R0
See All by H1R0
登壇するっきゃない!〜緊張しがちな私が初登壇に踏み切ったわけ〜
h1r0
1
520
Other Decks in Programming
See All in Programming
【やさしく解説 設計編 #0】DDDのコード、読めるのに分からない人へ
panda728
PRO
2
280
Lean は証明の正しさを確認するためだけのツールって思ってませんか?
inoueasei
1
110
Haskell/Servantを通してWebミドルウェアを捉え直す
pizzacat83
1
610
AIエージェントで 変わるAndroid開発環境
takahirom
2
720
GitHubCopilotCLIのスラッシュコマンドを自作してみる
htkym
0
110
【やさしく解説 設計編・中級 #6】良いアーキテクチャとは ~ 一本の登り道の、行き先 ~
panda728
PRO
0
180
Embedded SREと共に達成した会員管理システムのAWS移行 - SRE NEXT 2026 ランチスポンサーセッション
niftycorp
PRO
1
3k
琵琶湖の水は止められてもNet--HTTPのリトライは止められない / You might be able to stop the water flow of Lake Biwa but you can't stop Net::HTTP retries
luccafort
PRO
0
440
鹿野さんに聞く!『TypeScriptコードレシピ集』で磨く実践力
tonkotsuboy_com
4
1.2k
Claude Opus 4.6以後の受託開発エンジニアの変化(Claude Code開発ノウハウ大公開スペシャルbyクラスメソッド)
iidatakuma
1
860
変わらないものが、変わるものを決める — 意図駆動開発 × イベントソーシング × イミュータブル | What Doesn't Change Decides What Can — IDD × Event Sourcing × Immutability
tomohisa
0
310
PHPだって関数型したい 〜できること、できないこと〜 / fp-in-php
jsoizo
1
250
Featured
See All Featured
4 Signs Your Business is Dying
shpigford
187
22k
ピンチをチャンスに:未来をつくるプロダクトロードマップ #pmconf2020
aki_iinuma
128
56k
16th Malabo Montpellier Forum Presentation
akademiya2063
PRO
0
280
Side Projects
sachag
455
43k
Testing 201, or: Great Expectations
jmmastey
46
8.2k
Into the Great Unknown - MozCon
thekraken
41
2.6k
Test your architecture with Archunit
thirion
1
2.3k
Agile that works and the tools we love
rasmusluckow
331
22k
Fireside Chat
paigeccino
42
4k
The Director’s Chair: Orchestrating AI for Truly Effective Learning
tmiket
1
220
The browser strikes back
jonoalderson
0
1.4k
[SF Ruby Conf 2025] Rails X
palkan
2
1.2k
Transcript
⾞輪の再発明をしよう! PHP で実装して学ぶ、Web サーバーの仕組みと HTTP の正体 PHPerKaigi 2026
⾃⼰紹介 名前:本多 宏謙(H1R0) 所属:BABY JOB株式会社 肩書き:ギャルマインド伝道師(⾃称) 趣味:お酒、ゲーム @H1R0728 エンジニア⼈⽣のほとんどを PHP
と歩んできました
本発表のゴール • Web サーバーの基本的な仕組みが理解できる • ⾞輪の再発明をしたくなる
なんで PHP で Web サーバーを作ったのか そういえば Web サーバーの仕組みってどうなってるんだ? → ⾊んなドキュメントを読むも複雑で理解できず
→ 作った⽅が速いのでは?(AI ですぐ作れそうだし) → 慣れ親しんだ PHP のコードならすぐ理解できるはず!!
Gemini に実装させてみた <?php // 1. 80ポートで待ち受けるソケットの作成 $listening = stream_socket_server("tcp://0.0.0.0:80"); while
(true) { // 2. ブラウザからの接続を待機 $connected = stream_socket_accept($listening); if ($connected) { // 3. リクエストを読み取る $request = fread($connected, 1024); echo "--- Request Received ---\n" . $request; // 4. レスポンスを組み⽴てる $response = "HTTP/1.1 200 OK\r\n"; $response .= "Content-Type: text/html; charset=UTF-8\r\n"; $response .= "\r\n"; // ヘッダーとボディの区切り $response .= "<h1>Hello!</h1>"; // 5. ブラウザに送信して接続を閉じる fwrite($connected, $response); fclose($connected); } }
1. 待つ 接続を 待ち受ける 2. 読む リクエストを 読み取る 3. 返す
レスポンスを 組み⽴てて返す Web サーバーの仕事の 3 ステップ
1. 接続を待ち受ける
ソケットの作成 <?php // 1. 80ポートで待ち受ける(ソケットの作 成) $listening = stream_socket_server("tcp://0.0.0.0:80"); while
(true) { // 2. ブラウザからの接続を待機(accept) $connected = stream_socket_accept($listening); … } • stream_socket_server 指定した IP とポートに リスニングソケットを作る。 この関数ではソケットの作成 のみ⾏われる。
接続を待ち受ける <?php // 1. 80ポートで待ち受ける(ソケットの作 成) $listening = stream_socket_server("tcp://0.0.0.0:80"); while
(true) { // 2. ブラウザからの接続を待機(accept) $connected = stream_socket_accept($listening); … } • stream_socket_accept 接続が来るまで待機する。 接続が来ると接続済みソケット を作成し、接続へのストリーム を返す。 無限ループ内で実⾏すること で、後続の処理終了後、待機状 態に戻る。
ソケットとは • 通信の出⼊り⼝のこと ◦ クライアント側、サーバー側双⽅に存在する ◦ ソケットを通すことでデータをやり取りする ◦ IP アドレスとポート番号の組み合わせで通信相⼿を識別
• サーバー側にはリスニングソケットと接続済みソケットが存 在する
接続の待ち受け サーバー リスニングソケット クライアント1 クライアントソケット クライアント2 クライアントソケット 接続要求 接続要求
接続の受け⼊れ サーバー リスニングソケット クライアント1 クライアントソケット クライアント2 クライアントソケット 接続済みソケット1 接続済みソケット2 NEW!
NEW!
データの送受信開始 サーバー リスニングソケット クライアント1 クライアントソケット クライアント2 クライアントソケット 接続済みソケット1 接続済みソケット2 データ送受信
データ送受信
2. リクエストを読み取る
<?php while (true) { … if ($connected) { // 3.
リクエストを読み取る $request = fread($connected, 1024); echo "--- Request Received ---\n" . $request; … } } リクエストの読み取り fread で接続済みソケットの ストリームを読むことで、 リクエストの取得できる。 中⾝はテキストであり、RFC で 構造が定義されている。
実際のリクエストの中⾝(⼀部省略) POST / HTTP/1.1 Host: localhost Connection: keep-alive Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) Content-Type: text/plain Accept: text/html,application/xhtml+xml Accept-Encoding: gzip, deflate, br, zstd Accept-Language: ja,en-US;q=0.9,en;q=0.8,zh-TW;q=0.7,zh;q=0.6 hoge=piyo&user=h1r0
リクエストライン • メソッド、リクエストター ゲット、プロトコルバージョ ンで構成される • リクエストターゲットの形式 は 4 種類
POST / HTTP/1.1 Host: localhost Connection: keep-alive Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) Content-Type: text/plain Accept: text/html,application/xhtml+xml Accept-Encoding: gzip, deflate, br, zstd Accept-Language: ja,en-US;q=0.9,en;q=0.8,zh-TW;q=0.7,zh;q=0.6 hoge=piyo&user=h1r0
リクエストヘッダー • リクエストのメタデータを 設定する • Host ヘッダーは必須であり、 存在しない場合は 400 でレス
ポンスを返す必要がある • 改⾏区切り • クライアントが独⾃にキーを 設定することも可能 POST / HTTP/1.1 Host: localhost Connection: keep-alive Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) Content-Type: text/plain Accept: text/html,application/xhtml+xml Accept-Encoding: gzip, deflate, br, zstd Accept-Language: ja,en-US;q=0.9,en;q=0.8,zh-TW;q=0.7,zh;q=0.6 hoge=piyo&user=h1r0
リクエストボディ • 任意項⽬ • プレーンテキストや JSON など形式は様々 • Content-Length もしくは
Transfer-Encoding ヘッダー で⻑さを把握する POST / HTTP/1.1 Host: localhost Connection: keep-alive Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) Content-Type: text/plain Accept: text/html,application/xhtml+xml Accept-Encoding: gzip, deflate, br, zstd Accept-Language: ja,en-US;q=0.9,en;q=0.8,zh-TW;q=0.7,zh;q=0.6 hoge=piyo&user=h1r0
3. レスポンスを組み⽴てて返す
レスポンスの組み⽴て <?php while (true) { … if ($connected) { …
// 4. レスポンスを組み⽴てる $response = "HTTP/1.1 200 OK\r\n"; $response .= "Content-Type:text/html\r\n"; $response .= "\r\n"; // ヘッダーとボディの 区切り $response .= "<h1>Hello!</h1>"; } } リクエスト同様、中⾝はテキス トであり、RFC で定義された構 造に沿って設定する。
ステータスライン • プロトコルバージョン、ス テータスコード、ステータス コードの説明(任意項⽬)で 構成される <?php while (true) {
… if ($connected) { … $response = "HTTP/1.1 200 OK\r\n"; $response .= "Content-Type:text/html;\r\n"; $response .= "\r\n"; $response .= "<h1>Hello!</h1>"; … } }
レスポンスヘッダー • レスポンスのメタデータを 設定する • 必須のキーはない • 改⾏区切り • サーバー独⾃にキーを設定
することも可能 <?php while (true) { … if ($connected) { … $response = "HTTP/1.1 200 OK\r\n"; $response .= "Content-Type:text/html;\r\n"; $response .= "\r\n"; $response .= "<h1>Hello!</h1>"; … } }
レスポンスボディ • 任意項⽬ • プレーンテキストや JSON など形式は様々 • リクエストメソッド、ステー タスコード、Content-Length
もしくはTransfer-Encoding ヘッダーで⻑さを把握する <?php while (true) { … if ($connected) { … $response = "HTTP/1.1 200 OK\r\n"; $response .= "Content-Type:text/html;\r\n"; $response .= "\r\n"; $response .= "<h1>Hello!</h1>"; … } }
レスポンスの送信 fwrite で書き込むことでレスポ ンスとしてクライアントへ送信 される。 レスポンスの終了を明確にする ために fclose で接続を閉じる。 <?php
while (true) { … if ($connected) { … // 5. ブラウザに送信して接続を閉じる fwrite($connected, $response); fclose($connected); } }
リクエストとレスポンスの違い リクエスト レスポンス リクエストラインと ステータスラインの 構造 メソッド リクエストターゲット プロトコルバージョン プロトコルバージョン
ステータスコード ステータスコードの説明 必須ヘッダー Host なし ボディの⻑さを決定 するアルゴリズム Content-Length もしくは Transfer-Encoding ヘッダー リクエストメソッド ステータスコード 左記のヘッダー
HTTP のプロトコルバージョン 本⽇お話したのは HTTP/1.1(RFC 9112)の内容です。 現在は HTTP/2 と HTTP/3 があり、仕様が異なります。
詳細な仕様についてはそれぞれの RFC をご参照ください。 HTTP/2:RFC 9113 HTTP/3:RFC 9114
Web サーバーを作って感じたこと
⾞輪の再発明は無駄じゃない • 20 ⾏程度の簡単なものを作ることが、理解への近道だった ◦ 今⽇話した内容を理解するまでにかかった時間は、トー タル 1 週間程度 •
仕組みを理解することで、Web サーバーの設定値に対する 理解も深まった • 他にも⾊々作ってみたくなった ◦ DB サーバー作成予定
まとめ • Web サーバーの仕事は 3 ステップ ◦ 待つ、読む、返す • ソケットを使うことでクライアントとサーバーが通信できる
• HTTP/1.1 ではリクエストとレスポンスはテキストである • 普段当たり前に使っているものを⾃作することでより理解を 深められる
さあ、⾞輪を再発明しよう。
ご清聴ありがとうございました GitHub で実際に作った web サーバー公開してます。 https://github.com/H-H1RO/php-webserver