<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>PHP &#8211; エンジニア見習い</title>
	<atom:link href="https://otonan-syusyoku.work/archives/tag/php/feed" rel="self" type="application/rss+xml" />
	<link>https://otonan-syusyoku.work</link>
	<description>三流プログラマー</description>
	<lastBuildDate>Wed, 25 Feb 2026 08:34:10 +0000</lastBuildDate>
	<language>ja</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.0</generator>

<image>
	<url>https://otonan-syusyoku.work/wp-content/uploads/2023/10/cropped-名称未設定のデザイン-16-32x32.png</url>
	<title>PHP &#8211; エンジニア見習い</title>
	<link>https://otonan-syusyoku.work</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>とりあえず出力していたログから意図を持ったログにするために</title>
		<link>https://otonan-syusyoku.work/archives/2189</link>
					<comments>https://otonan-syusyoku.work/archives/2189#respond</comments>
		
		<dc:creator><![CDATA[hrokig2]]></dc:creator>
		<pubDate>Wed, 07 Jan 2026 02:42:51 +0000</pubDate>
				<category><![CDATA[生涯独学]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[実務]]></category>
		<guid isPermaLink="false">https://otonan-syusyoku.work/?p=2189</guid>

					<description><![CDATA[「ログ、とりあえず出してますか？」 正直に告白します。僕はこれまで、なんとなくログを出していました。「エラーが起きたら怖いから、とりあえず try-catch してログに残しておこう」「ないよりはマシだろう」 でも、いざ [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">「ログ、とりあえず出してますか？」</p>



<p class="wp-block-paragraph">正直に告白します。僕はこれまで、なんとなくログを出していました。<br>「エラーが起きたら怖いから、とりあえず <code>try-catch</code> してログに残しておこう」<br>「ないよりはマシだろう」</p>



<p class="wp-block-paragraph">でも、いざ本番で障害が起きたとき、そのログは僕を助けてくれませんでした。<br>深夜のアラート対応で、システムエラーが発生しました とだけ書かれたログを前に途方に暮れた経験、皆さんにもありませんか？</p>



<p class="wp-block-paragraph">今回は、そんな「なんとなくログ」を卒業し、トラブルシューティングや分析に本当に役立つ「使えるログ」を設計するために学んだことをまとめました。</p>



<h2 class="wp-block-heading">ログの目的を再定義する</h2>



<p class="wp-block-paragraph">そもそも、なぜログを書くのでしょうか？ チームで決まっているからではなく、エンジニアとして以下の3つの目的を意識する必要があります。</p>



<ol start="1" class="wp-block-list">
<li><strong>トラブルシューティング（Why &amp; Where?）</strong>
<ul class="wp-block-list">
<li>「なぜ落ちたのか」「どこで落ちたのか」を特定し、バグを再現・修正するため。</li>
</ul>
</li>



<li><strong>可観測性・分析（Observability）</strong>
<ul class="wp-block-list">
<li>「この機能、どれくらい使われてる？」「処理に何秒かかってる？」といったシステムの健康状態やユーザー行動を知るため。</li>
</ul>
</li>



<li><strong>監査・セキュリティ（Security）</strong>
<ul class="wp-block-list">
<li>「いつ、誰が、重要なデータを変更したか」の証跡を残すため。</li>
</ul>
</li>
</ol>



<p class="wp-block-paragraph">これらを意識すると、<strong>「とりあえず出力」ではなく「目的に合わせて出力」する必要がある</strong>ことに気づきます。</p>



<h2 class="wp-block-heading">構造化ログってなんや</h2>



<p class="wp-block-paragraph">ログ設計を学ぶと必ず出てくるのが<strong>「構造化ログ（Structured Logging）」</strong>です。</p>



<p class="wp-block-paragraph">構造化ログとは<strong>「ログを人間への手紙ではなく、機械へのデータとして扱う」</strong>ということです。</p>



<h3 class="wp-block-heading">非構造化ログ</h3>



<p class="wp-block-paragraph">従来のテキスト形式のログです。</p>



<pre class="wp-block-code"><code>// 人間には読めるけど、機械（検索）には辛い
Log::error("User 123 failed to login from 192.168.0.1");
</code></pre>



<p class="wp-block-paragraph">これだと、「ユーザーID 123 のエラーだけ集計したい」と思ったときに、正規表現で頑張って文字列解析をしなければなりません。<br>ログの文言が少し変わっただけで検索できなくなるので、運用が非常に辛くなります。</p>



<h3 class="wp-block-heading">構造化ログ</h3>



<p class="wp-block-paragraph">ログをJSONなどの形式で出力します。</p>



<pre class="wp-block-code"><code>// メッセージとデータを分ける
Log::error("Login failed", &#91;
    "user_id" =&gt; 123,
    "ip_address" =&gt; "192.168.0.1",
    "event" =&gt; "auth_error"
]);
</code></pre>



<p class="wp-block-paragraph">これが出力されると、以下のようなJSONになります。</p>



<pre class="wp-block-code"><code>{
  "level": "ERROR",
  "message": "Login failed",
  "context": {
    "user_id": 123,
    "ip_address": "192.168.0.1",
    "event": "auth_error"
  },
  "datetime": "2024-01-07T12:00:00+09:00"
}
</code></pre>



<p class="wp-block-paragraph">こうしておけば、DatadogやCloudWatch Logsなどのログ管理ツールで <code>context.user_id = 123</code> のようにクエリ一発で検索・集計ができます。</p>



<p class="wp-block-paragraph">現代のWeb開発では、<strong>ログは「読むもの」ではなく「検索・集計するもの」</strong>です。<br><strong>構造化ログはマスト</strong>と言えます。</p>



<h2 class="wp-block-heading">ログ設計の実践</h2>



<p class="wp-block-paragraph">では、実際にコードを書くときに何を意識すべきでしょうか。</p>



<h3 class="wp-block-heading">やるべきこと</h3>



<h4 class="wp-block-heading">コンテキスト（文脈）を含める</h4>



<p class="wp-block-paragraph">「エラーです」だけでは無意味です。<br>「その時何が起きていたか」の情報を連想配列（Context）として渡しましょう。</p>



<p class="wp-block-paragraph">何が起こったかを把握するためにも 5W1H を意識するといいかもですね。</p>



<ul class="wp-block-list">
<li><strong>誰が？</strong> (User ID)</li>



<li><strong>何を？</strong> (Input Data)</li>



<li><strong>どこで？</strong> (Request URL, IP Address)</li>
</ul>



<h4 class="wp-block-heading">トレースID (Trace ID / Request ID) を通す</h4>



<p class="wp-block-paragraph">これが今回一番の学びでした。 <br>「1回のリクエスト」に対して、ユニークなID（Trace ID）を割り当て、それを全てのログに含めます。</p>



<pre class="wp-block-code"><code>// 理想的なログ出力
{
    "message": "DB error",
    "request_id": "a0eebc99-9c0b...",  // これ
    "user_id": 101
}
</code></pre>



<p class="wp-block-paragraph">これがあれば、マイクロサービスや非同期処理でログがバラバラになっても、<strong>IDで検索して「一連の処理の流れ」</strong>を追うことができます。</p>



<p class="wp-block-paragraph">Laravelなどでは、ミドルウェアで自動的にIDを発行し、<code>Log::shareContext()</code> 等を使って全ログに自動付与する設定を入れておくと、コードを書くときに意識しなくて済むので最高です。</p>



<h4 class="wp-block-heading">ログレベルを適切に使い分ける</h4>



<p class="wp-block-paragraph">チーム内の基準に従いましょう。ない場合は基準を決めておきましょう。</p>



<p class="wp-block-paragraph">下記は一例です。</p>



<ul class="wp-block-list">
<li><strong>ERROR:</strong> 即時対応が必要（深夜でも電話がかかってくるレベル）。</li>



<li><strong>WARN:</strong> 異常だがシステムは稼働継続可能（後で要確認）。</li>



<li><strong>INFO:</strong> 正常系イベント（KPI分析や動作確認用）。</li>



<li><strong>DEBUG:</strong> 開発時のデバッグ用（本番では出さない）。</li>
</ul>



<h3 class="wp-block-heading">アンチパターン</h3>



<h4 class="wp-block-heading">機密情報の出力</h4>



<p class="wp-block-paragraph">パスワード、アクセストークン、クレジットカード番号、個人情報（PII）をログに出してはいけません。</p>



<p class="wp-block-paragraph">ログファイル自体が漏洩したときのリスクが甚大です。平文なのでね。</p>



<h4 class="wp-block-heading">「とりあえずcatchしてログ」による握りつぶし</h4>



<p class="wp-block-paragraph">一番やりがちなやつです。</p>



<pre class="wp-block-code"><code>try {
    $user-&gt;save();
} catch (Exception $e) {
    // 最悪なパターン：エラーが起きた事実だけログして、処理を続行してしまう
    Log::error("Error happened"); 
}
</code></pre>



<p class="wp-block-paragraph">これだと、データ不整合が起きているのにシステムが動き続け、後で原因不明のバグになります。<br>対処できないエラーはログに出すだけでなく、適切に例外を再スローするか、エラーレスポンスを返す必要があります。</p>



<h2 class="wp-block-heading">ログ運用</h2>



<p class="wp-block-paragraph">「ログをどこに出すか」も重要です。</p>



<p class="wp-block-paragraph">DockerやKubernetesなどのコンテナ環境では、<strong>「標準出力（stdout/stderr）に吐く」</strong>のが定石です（<a href="https://12factor.net/ja/logs">The 12-Factor App</a>の思想）。</p>



<p class="wp-block-paragraph">アプリは標準出力にJSONを流すだけ。あとはFluentdやDatadog Agentがそれを拾って、S3やログ管理基盤に転送する。アプリは「ログの保存場所」に関知しない設計にするのがモダンな運用です。</p>



<h2 class="wp-block-heading">5. ログローテート</h2>



<p class="wp-block-paragraph">最後に、地味だけど大事な「お掃除」の話です。 もしファイルにログを出力する場合、書き続けるといつかディスクが溢れます。数GBのテキストファイルを開こうとしてエディタが固まった経験、ありますよね？<br>（いつぞやに対応したEC2のアラート対応で、15GBのログファイルがサーバーを圧迫していた事件がありました。）</p>



<ul class="wp-block-list">
<li><strong>ローテート（Rotation）：</strong> 日次やサイズ指定でファイルを分割する（例: <code>app.log</code> -> <code>app.log.2024-01-07</code>）。</li>



<li><strong>保持期間（Retention）：</strong> 「14日経過したら削除する」といったルールを決める。</li>
</ul>



<p class="wp-block-paragraph">PHP（Monolog）なら <code>RotatingFileHandler</code> を使うことで簡単に実装できますし、Linuxの <code>logrotate</code> コマンドで管理することもあります。</p>



<p class="wp-block-paragraph">「ディスクフルでサーバーダウン」は一番悲しい障害なので、リリース前に必ず確認しましょう。</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">おわりに</h2>



<p class="wp-block-paragraph">ログ設計を見直すことは、「未来の自分やチームメイトへの思いやり」です。</p>



<p class="wp-block-paragraph">障害発生時、適切なログ（構造化され、Trace IDがあり、必要な情報が詰まっているログ）があれば数分で解決できる問題が、ログがないために数日かかることもあります。</p>



<p class="wp-block-paragraph">「何かあったときに、このログを見て原因がわかるか？」 「一年後の自分がこのログを見て感謝するか？」</p>



<p class="wp-block-paragraph">そう自問しながら、明日からの <code>Log::info()</code> を書いていきたいと思います。</p>



<p class="wp-block-paragraph"></p>
]]></content:encoded>
					
					<wfw:commentRss>https://otonan-syusyoku.work/archives/2189/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>2025年、AIを使った開発で役立ったTipsたち</title>
		<link>https://otonan-syusyoku.work/archives/2187</link>
					<comments>https://otonan-syusyoku.work/archives/2187#respond</comments>
		
		<dc:creator><![CDATA[hrokig2]]></dc:creator>
		<pubDate>Sat, 27 Dec 2025 10:10:09 +0000</pubDate>
				<category><![CDATA[仕事の独り言]]></category>
		<category><![CDATA[生涯独学]]></category>
		<category><![CDATA[DevOps]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Typescript]]></category>
		<category><![CDATA[ポエム]]></category>
		<guid isPermaLink="false">https://otonan-syusyoku.work/?p=2187</guid>

					<description><![CDATA[AIによるコーディング支援は、もう「当たり前」の景色になりました。 AIに頼めば、ものの数秒で大量のコードが生成されます。まるで魔法のようですが、同時にこうも思うのです。「これ、本当に動くの？」「仕様、合ってる？」と。  [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">AIによるコーディング支援は、もう「当たり前」の景色になりました。</p>



<p class="wp-block-paragraph">AIに頼めば、ものの数秒で大量のコードが生成されます。<br>まるで魔法のようですが、同時にこうも思うのです。「これ、本当に動くの？」「仕様、合ってる？」と。</p>



<p class="wp-block-paragraph">大量に出力されるコードの波に飲まれず、<strong>「人間が手綱を握り続ける」</strong>ために僕が実践してきた開発のTipsを書き残しておきます。</p>



<p class="wp-block-paragraph">これは、2026年の自分への手紙でもあります。<br>「あの頃はそんな苦労をしてたんだな」と笑って読み返せますように。</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">機会にできることは機械に任せる（Lint / Pre-commit / Auto Format）</h2>



<p class="wp-block-paragraph">AIは優秀ですが、たまにインデントを崩したり、変な書き癖を出してきたりします。<br>生成された大量のコードを目視でレビューして「ここ、スペース空いてないよ」なんて指摘するのは、人間のやる仕事じゃありませんよね。</p>



<p class="wp-block-paragraph">だからこそ、<strong>Lint</strong> と <strong>Pre-commit</strong>、そして <strong>自動フォーマット</strong> は必須でした。</p>



<ul class="wp-block-list">
<li><strong>保存＝整形（Auto Format）</strong><br>エディタで保存した瞬間に、コードが綺麗になる設定はマスト。AIが吐き出したコードを貼り付けた瞬間、プロジェクトの規約に合わせて「シュッ」と整う快感。これがないと精神衛生が保てません。</li>



<li><strong>コミット前の門番（Pre-commit）</strong><br> Gitにコミットする前に、Linterが自動で走るように設定しました。型エラーや未使用変数は、人間が気づく前に機械に弾いてもらう。</li>
</ul>



<p class="wp-block-paragraph"><strong>「コードの見た目」や「単純なミス」に脳のメモリを使わない。</strong>これがAI時代を生き抜く第一歩でした。（AI開発がどうっていう話ではないがより一層重要になっている</p>



<p class="wp-block-paragraph"></p>



<h2 class="wp-block-heading">実装指示は細かくStepByStep形式</h2>



<p class="wp-block-paragraph">最終的なゴールを明示したうえで、StepByStepで実装させました。</p>



<p class="wp-block-paragraph">ゴールは <code>.documents</code> にMarkdownを配置し、その仕様書通りになるように指示をします。</p>



<pre class="wp-block-code"><code>## As Is

## To Be 

## Task

## Remarks</code></pre>



<ol class="wp-block-list">
<li>〇〇を最終ゴールとします。実装方針を出して。</li>



<li>ツッコミを入れる
<ul class="wp-block-list">
<li>Step1の実装のセキュリティホールは？</li>



<li>タイムアウトの条件は〇〇に</li>
</ul>
</li>



<li>テスト書いてもらう</li>



<li>もちろんRed</li>



<li>テストのレビュー
<ul class="wp-block-list">
<li>テスト問題なければ実装</li>



<li>問題あれば2を見直す</li>
</ul>
</li>



<li>テストがGreenになるような実装</li>
</ol>



<p class="wp-block-paragraph">この辺はガイドラインやコーディング規約などを整備すればよかったのかなぁと考えている反省点です。</p>



<h2 class="wp-block-heading">テスト戦略：AIに任せる部分、僕が譲らない部分</h2>



<p class="wp-block-paragraph">ここが一番のキモです。<br> AIはコードを書くのは早いですが、<strong>「僕たちが本当に作りたいもの」</strong>を100%理解しているわけではありません。<br>だからテストの役割分担をこう決めました。</p>



<h3 class="wp-block-heading">単体テスト（ViteTest / PHPUnit）</h3>



<p class="wp-block-paragraph">関数単体や小さなロジックの検証には、ViteTestやPHPUnitを使いました。<br>ここはAIにも手伝ってもらいやすい領域です。「この関数のテスト書いて」と言えば、エッジケースまで網羅したテストを提案してくれます。<br><br>※ 単体テストをAIに書いてもらうために、関数は限りなく小さくするような単一責任の原則でAIに実装してもらっています。ぜーんぶAIです。</p>



<h3 class="wp-block-heading">Featureテスト（結合・機能テスト）は「僕」が書く</h3>



<p class="wp-block-paragraph">けれど、機能全体が正しく動くかを確認する<strong>Featureテストだけは、僕自身の手で書く</strong>ようにしました。<br>これには明確な理由が2つあります。</p>



<ol start="1" class="wp-block-list">
<li><strong>コードの理解</strong>：<br>AIが書いたブラックボックスなコードも、テストを書く過程で読み解く必要があります。「どう動くべきか」を定義するのは人間です。</li>



<li><strong>要求仕様の達成</strong>：<br>求められている「要件」を満たしているか判定できるのは、今のところまだ人間だけです。</li>
</ol>



<p class="wp-block-paragraph"><strong>「AIにコードを書かせるなら、人間はテスト（仕様）を書け」</strong> これが2025年の僕の合言葉でした。<br>テストが通る＝仕様を満たしているという安心感があって初めて、AIのスピードを享受できるんです。</p>



<h2 class="wp-block-heading">2026年の僕へ</h2>



<p class="wp-block-paragraph">どうですか？ 2026年の開発現場は。</p>



<p class="wp-block-paragraph">AIが仕様の矛盾すら指摘して、テストまで完ぺきに書いてくれるようになっているのでしょうか。</p>



<p class="wp-block-paragraph">もしそうなっていたとしても、2025年の僕は「正しく動くものを届けたい」という一心で、Linterを設定し、Featureテストを書いていたことを覚えていてください。</p>



<p class="wp-block-paragraph">エンジニアとしての仕事に楽しさは残っていますか？2025年末では、だんだんと辛くなってきています。</p>



<p class="wp-block-paragraph">それじゃ、また。良い開発ライフを！</p>
]]></content:encoded>
					
					<wfw:commentRss>https://otonan-syusyoku.work/archives/2187/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>マルチステージビルドでイメージサイズを劇的に削減できるべ</title>
		<link>https://otonan-syusyoku.work/archives/2165</link>
					<comments>https://otonan-syusyoku.work/archives/2165#respond</comments>
		
		<dc:creator><![CDATA[hrokig2]]></dc:creator>
		<pubDate>Wed, 19 Nov 2025 04:09:20 +0000</pubDate>
				<category><![CDATA[インフラ]]></category>
		<category><![CDATA[DevOps]]></category>
		<category><![CDATA[Docker]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[業務]]></category>
		<guid isPermaLink="false">https://otonan-syusyoku.work/?p=2165</guid>

					<description><![CDATA[PHPアプリケーションのコンテナイメージを作成する際、ビルドに必要なツール（Composer、各種コンパイラなど）をすべて含めてしまうと、イメージが肥大化し、セキュリティリスクも増大します。 この課題を解決するのが、コン [&#8230;]]]></description>
										<content:encoded><![CDATA[<div id="model-response-message-contentr_700d418a97f56345" class="markdown markdown-main-panel stronger enable-updated-hr-color" dir="ltr" aria-live="polite" aria-busy="false">
<p>PHPアプリケーションのコンテナイメージを作成する際、ビルドに必要なツール（Composer、各種コンパイラなど）をすべて含めてしまうと、イメージが肥大化し、セキュリティリスクも増大します。</p>
<p>この課題を解決するのが、コンテナビルドの効率を最大化する技術、<b>マルチステージビルド</b>です。</p>
<h2>マルチステージビルドとは？</h2>
<p>マルチステージビルドとは、<b>一つの<code>Dockerfile</code>内で複数の<code>FROM</code>命令（ステージ）を定義し、最終的なイメージにデプロイに必要なファイルだけをコピーする</b>手法です。</p>
<h3>役割分担</h3>
<ol start="1">
<li><b>ビルドステージ (Build Stage):</b>
<ul>
<li><b>目的:</b> アプリケーションのビルドと依存関係の解決。</li>
<li><b>使用イメージ:</b> <b>大きなイメージ</b>（例: <code>composer:latest</code>、<code>php:8.3-fpm</code>など）。</li>
<li><b>作業:</b> Composerによる依存パッケージのインストール、フロントエンドアセットのコンパイル、テストの実行などを行います。</li>
<li><b>特徴:</b> このステージは最終的なデプロイには使用されません。</li>
</ul>
</li>
<li><b>最終ステージ (Final Stage):</b>
<ul>
<li><b>目的:</b> アプリケーションの実行環境の構築。</li>
<li><b>使用イメージ:</b> <b>軽量なイメージ</b>（例: <code>php:8.3-fpm-alpine</code>、<code>distroless</code>など）。</li>
<li><b>作業:</b> ビルドステージで生成された<b>成果物</b>（例: <code>vendor</code>ディレクトリ、コンパイル済みバイナリ、PHPコード）のみをコピーします。</li>
</ul>
</li>
</ol>
<h3>メリット</h3>
<ul>
<li><b>イメージサイズの劇的な削減:</b> ビルドツールやキャッシュ、開発用の依存関係が最終イメージから完全に除外されます。</li>
<li><b>セキュリティ向上:</b> 最終イメージが最小限の構成になるため、攻撃対象となる領域が縮小します。</li>
<li><b>ビルド時間の短縮:</b> 軽量なイメージを使用することで、プル時間が短くなります。</li>
</ul>
<h2>PHPアプリケーションでのマルチステージビルド</h2>
<p>PHPアプリケーションの典型的なマルチステージビルドは、<b>Composerによる依存関係の解決</b>を分離することに焦点を当てます。</p>
<h3>Dockerfile</h3>
<p>以下の<code>Dockerfile</code>は、Composerで依存関係を解決するステージと、それを使ってアプリケーションを実行する最小限のステージに分離しています。</p>
<pre class="ng-tns-c519592647-2110"><code># =============================================</code>
<code># ステージ1：build_stage（依存関係の解決）</code>
<code># =============================================</code>
<code>FROM composer:latest AS build_stage 
WORKDIR /app 
# 先に依存関係ファイルだけをコピー（キャッシュ効率化のため）
COPY composer.json composer.lock ./ 
# 本番環境用に最適化してインストール 
</code><code># --no-dev: 開発用パッケージを除外</code>
<code># --optimize-autoloader: オートローダーを最適化して高速化 
</code><code>RUN composer install \</code>
<code>--no-dev \</code>
<code>--no-interaction \</code>
<code>--optimize-autoloader 
</code><code># =============================================</code>
<code># ステージ2：final_stage（最小限の実行環境）</code>
<code># =============================================</code>
<code># 軽量なAlpine LinuxベースのPHP-FPMイメージを使用</code>
<code>FROM php:8.3-fpm-alpine AS final_stage 
</code><code># セキュリティ：実行用の非特権ユーザーを作成</code>
<code>RUN adduser -D appuser</code>
<code>USER appuser
</code><code>WORKDIR /var/www/html
</code><code># ステージ1からvendorディレクトリのみをコピー</code>
<code># --chown で所有権を同時に変更するのがポイント</code>
<code>COPY --from=build_stage --chown=appuser:appuser /app/vendor ./vendor
</code><code># アプリケーションのソースコードをコピー</code>
<code>COPY --chown=appuser:appuser . .
 </code>
<code># PHP-FPMを起動 
</code><code>EXPOSE 9000</code><code>
CMD ["php-fpm"]</code><span style="font-size: 14px; background-color: #dddddd;">

</span></pre>
</div>
<h3>実行手順</h3>
<ol start="1">
<li><b>プロジェクトルートに配置:</b> 上記の<code>Dockerfile</code>をプロジェクトのルートディレクトリに配置します。</li>
<li><b><code>docker build</code>を実行:</b> 通常通り<code>docker build</code>コマンドを実行するだけです。Dockerエンジンが自動的にマルチステージビルドを処理します。</li>
</ol>
<pre class="line-numbers"><code class="language-php">docker build -t my-php-app:prod .</code></pre>
<h3>PHPでのポイント</h3>
<ol start="1">
<li><b><code>composer_stage</code>の利用:</b> Composerをインストールする手間を省くため、公式の<code>composer</code>イメージをビルダーとして使うのが最も効率的です。</li>
<li><b><code>--no-dev</code>:</b> 開発時のみ必要なパッケージ（テストフレームワークなど）は最終イメージに含めないようにします。</li>
<li><b><code>USER nonroot</code>:</b> セキュリティのため、最終ステージでは必ず<code>root</code>権限を放棄し、<b>非特権ユーザー</b>でアプリケーションを実行するように設定しましょう。（これにより、低ポート番号の使用には制約が生じますが、セキュリティが大幅に向上します）</li>
<li><b><code>COPY --from</code>の活用:</b> この命令が全てです。Composerの巨大なキャッシュやビルドツール群を最終イメージから排除し、必要な<code>vendor</code>ディレクトリだけをクリーンにコピーします。</li>
</ol>
<p>マルチステージビルドは、軽量で安全なPHPコンテナイメージを作成するための必須技術です。ぜひあなたのプロジェクトに導入してください。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://otonan-syusyoku.work/archives/2165/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Laravel+Inertia+ReactをECSで動かしたい！ NginxとPHP-FPMのコンテナ分離</title>
		<link>https://otonan-syusyoku.work/archives/2153</link>
					<comments>https://otonan-syusyoku.work/archives/2153#respond</comments>
		
		<dc:creator><![CDATA[hrokig2]]></dc:creator>
		<pubDate>Tue, 28 Oct 2025 05:24:52 +0000</pubDate>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[インフラ]]></category>
		<category><![CDATA[AWS]]></category>
		<category><![CDATA[DevOps]]></category>
		<category><![CDATA[Docker]]></category>
		<category><![CDATA[LAMP]]></category>
		<category><![CDATA[Laravel]]></category>
		<category><![CDATA[nginx]]></category>
		<category><![CDATA[React]]></category>
		<category><![CDATA[ネットワーク]]></category>
		<guid isPermaLink="false">https://otonan-syusyoku.work/?p=2153</guid>

					<description><![CDATA[Laravel + Inertia (React/Vue) の組み合わせは、SPA（シングルページアプリケーション）の体験とサーバーサイド（Laravel）の書きやすさを両立できる、非常に強力な構成です。 しかし、ローカ [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>Laravel + Inertia (React/Vue) の組み合わせは、SPA（シングルページアプリケーション）の体験とサーバーサイド（Laravel）の書きやすさを両立できる、非常に強力な構成です。</p>
<p>しかし、ローカルのDocker Compose環境では問題なく動作しても、AWSのECS Fargateのような本番環境にデプロイしようとすると、いくつかの疑問に直面します。</p>
<ul>
<li>「NginxとPHP-FPMコンテナはどう連携させるのがベスト？」</li>
<li>「<code>fastcgi_pass</code> ってローカルと設定を変える必要ある？」</li>
<li>「InertiaでビルドしたReactのJS/CSSファイルは、結局どのコンテナに置くのが正解？」</li>
</ul>
<p>この記事では、<code>fastcgi_pass</code> の基本から、ECS Fargateでの最適なコンテナ構成、そしてInertia/Reactプロジェクト特有の「静的ファイルの配置場所」問題までを、順を追って解説します。</p>
<h2>fastcgi_pass とは？</h2>
<h3>NginxとPHP-FPMの「橋渡し」</h3>
<p>まず基本のおさらいです。Nginxは高性能なWebサーバーですが、それ自体はPHPコードを実行できません。<br />
一方、PHP-FPMはPHPコードを実行することに特化したプロセス（サーバー）です。</p>
<p>Nginxがクライアント（ブラウザ）から <code>.php</code> ファイルへのリクエストを受け取ったとき、そのリクエストをPHP-FPMに処理してもらう必要があります。</p>
<p>このとき、Nginxの設定ファイル（<code>nginx.conf</code>）で、<b>「どのPHP-FPMに処理を依頼するか」の宛先を指定する</b>のが <code>fastcgi_pass</code> ディレクティブです。</p>
<div _ngcontent-ng-c535465467="" class="code-block ng-tns-c535465467-470 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" jslog="223238;track:impression,attention;BardVeMetadataKey:[[&quot;r_4fea32b244968dc8&quot;,&quot;c_103da6cb53c10bc2&quot;,null,&quot;rc_8751fdc7f6beda59&quot;,null,null,&quot;ja&quot;,null,1,null,null,1,0]]">
<div _ngcontent-ng-c535465467="" class="formatted-code-block-internal-container ng-tns-c535465467-470">
<div _ngcontent-ng-c535465467="" class="animated-opacity ng-tns-c535465467-470">
<pre _ngcontent-ng-c535465467="" class="ng-tns-c535465467-470"><code _ngcontent-ng-c535465467="" role="text" data-test-id="code-content" class="code-container formatted ng-tns-c535465467-470"><span class="hljs-attribute">location</span> <span class="hljs-regexp">~ \.php$</span> {
    <span class="hljs-comment"># ...</span>
    <span class="hljs-comment"># ↓ この一行が「橋渡し」の指定</span>
    <span class="hljs-attribute">fastcgi_pass</span> php-fpm-server:<span class="hljs-number">9000</span>; 
}
</code></pre>
</div>
</div>
</div>
<p>&nbsp;</p>
<h2>ローカル開発 (Docker Compose) での構成</h2>
<p>ローカル開発では、「1コンテナ1責務」の原則に従い、NginxとPHP-FPMを別々のコンテナとして <code>docker-compose.yml</code> で定義するのが一般的です。</p>
<div _ngcontent-ng-c535465467="" class="code-block ng-tns-c535465467-471 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" jslog="223238;track:impression,attention;BardVeMetadataKey:[[&quot;r_4fea32b244968dc8&quot;,&quot;c_103da6cb53c10bc2&quot;,null,&quot;rc_8751fdc7f6beda59&quot;,null,null,&quot;ja&quot;,null,1,null,null,1,0]]">
<div _ngcontent-ng-c535465467="" class="formatted-code-block-internal-container ng-tns-c535465467-471">
<div _ngcontent-ng-c535465467="" class="animated-opacity ng-tns-c535465467-471">
<pre _ngcontent-ng-c535465467="" class="ng-tns-c535465467-471"><code _ngcontent-ng-c535465467="" role="text" data-test-id="code-content" class="code-container formatted ng-tns-c535465467-471"><span class="hljs-comment"># docker-compose.yml (抜粋)</span>
<span class="hljs-attr">version:</span> <span class="hljs-string">'3'</span>
<span class="hljs-attr">services:</span>
  <span class="hljs-comment"># Nginxコンテナ</span>
  <span class="hljs-attr">nginx:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">nginx:alpine</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"80:80"</span>
    <span class="hljs-attr">volumes:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">./nginx.conf:/etc/nginx/conf.d/default.conf</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">./laravel-project:/var/www/html</span>

  <span class="hljs-comment"># PHP-FPMコンテナ</span>
  <span class="hljs-attr">php:</span>
    <span class="hljs-attr">build:</span> <span class="hljs-string">.</span> <span class="hljs-comment"># PHP-FPMとLaravelコードを含むDockerfile</span>
    <span class="hljs-attr">volumes:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">./laravel-project:/var/www/html</span>
</code></pre>
</div>
</div>
</div>
<p>&nbsp;</p>
<p>この構成では、NginxコンテナとPHPコンテナは <b>別々のネットワーク空間</b> に存在します。Docker Composeが提供する内部ネットワークを介して、サービス名で通信します。</p>
<p>したがって、NginxコンテナからPHPコンテナへは、<code>php</code> というサービス名（ホスト名）を使ってアクセスします。</p>
<div _ngcontent-ng-c535465467="" class="code-block ng-tns-c535465467-472 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" jslog="223238;track:impression,attention;BardVeMetadataKey:[[&quot;r_4fea32b244968dc8&quot;,&quot;c_103da6cb53c10bc2&quot;,null,&quot;rc_8751fdc7f6beda59&quot;,null,null,&quot;ja&quot;,null,1,null,null,1,0]]">
<div _ngcontent-ng-c535465467="" class="formatted-code-block-internal-container ng-tns-c535465467-472">
<div _ngcontent-ng-c535465467="" class="animated-opacity ng-tns-c535465467-472">
<pre _ngcontent-ng-c535465467="" class="ng-tns-c535465467-472"><code _ngcontent-ng-c535465467="" role="text" data-test-id="code-content" class="code-container formatted ng-tns-c535465467-472"><span class="hljs-comment"># nginx.conf (Docker Compose用)</span>
<span class="hljs-attribute">location</span> <span class="hljs-regexp">~ \.php$</span> {
    <span class="hljs-comment"># ...</span>
    <span class="hljs-comment"># 'php' サービス（コンテナ）の 9000番ポートに転送</span>
    <span class="hljs-attribute">fastcgi_pass</span> php:<span class="hljs-number">9000</span>;
}
</code></pre>
</div>
</div>
</div>
<p>&nbsp;</p>
<p>（※ このTCP/IP通信によるパフォーマンスやセキュリティの懸念は、コンテナ間通信がDockerの内部ネットワークに限定されていれば、実用上ほとんど問題になりません。）</p>
<h2>【1タスク2コンテナ】本番 (ECS Fargate) での構成：</h2>
<p>さて、本題のECS Fargateです。<br />
ECSでは、<code>docker-compose.yml</code> に相当するものとして「タスク定義 (Task Definition)」を使います。</p>
<p>ここで重要なキーポイントは、<strong>「1つのタスク定義の中で、NginxコンテナとPHP-FPMコンテナの2つを定義する」</strong>ことです。</p>
<p><strong>これら2つのコンテナは、1セットで「Webアプリケーションサーバー」</strong>として機能します。</p>
<h3>ECS Fargateでのfastcgi_passはどうなる？</h3>
<p>ECSの同一タスク内で実行されるコンテナは、<b>同じネットワーク空間（ネットワークモード <code>awsvpc</code>）を共有します</b>。</p>
<p>これは、Nginxコンテナから見ると、PHP-FPMコンテナが「他人」ではなく、「自分自身（localhost）」として見えることを意味します。</p>
<p>したがって、ECS Fargate用のNginxイメージに含める <code>nginx.conf</code> の設定は、以下のようになります。</p>
<div _ngcontent-ng-c535465467="" class="code-block ng-tns-c535465467-473 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" jslog="223238;track:impression,attention;BardVeMetadataKey:[[&quot;r_4fea32b244968dc8&quot;,&quot;c_103da6cb53c10bc2&quot;,null,&quot;rc_8751fdc7f6beda59&quot;,null,null,&quot;ja&quot;,null,1,null,null,1,0]]">
<div _ngcontent-ng-c535465467="" class="formatted-code-block-internal-container ng-tns-c535465467-473">
<div _ngcontent-ng-c535465467="" class="animated-opacity ng-tns-c535465467-473">
<pre _ngcontent-ng-c535465467="" class="ng-tns-c535465467-473"><code _ngcontent-ng-c535465467="" role="text" data-test-id="code-content" class="code-container formatted ng-tns-c535465467-473"><span class="hljs-comment"># nginx.conf (ECS Fargate用)</span>
<span class="hljs-attribute">location</span> <span class="hljs-regexp">~ \.php$</span> {
    <span class="hljs-comment"># ...</span>
    <span class="hljs-comment"># サービス名ではなく、localhost (127.0.0.1) を指定する</span>
    <span class="hljs-attribute">fastcgi_pass</span> <span class="hljs-number">127.0.0.1:9000</span>;
}
</code></pre>
</div>
</div>
</div>
<p>&nbsp;</p>
<h3>タスク定義のイメージ</h3>
<p>タスク定義では、以下のように設定します。</p>
<ul>
<li><b>コンテナ-A: <code>php-fpm-container</code></b>
<ul>
<li>イメージ: （Laravelコードを含むPHP-FPMイメージ）</li>
<li>ポートマッピング: <b>なし</b> (外部に公開する必要はないため)</li>
</ul>
</li>
<li><b>コンテナ-B: <code>nginx-container</code></b>
<ul>
<li>イメージ: （設定済みのnginx.confと静的ファイルを含むNginxイメージ）</li>
<li>ポートマッピング: <b><code>80:80</code></b> (ALBからのトラフィックを受け取るため)</li>
</ul>
</li>
</ul>
<p>ALB（ロードバランサー）からのトラフィックはNginxコンテナが受け取り、必要に応じて <code>localhost:9000</code> を介してPHP-FPMコンテナに処理を渡します。</p>
<h2>Inertia/Reactのビルドファイルはどこに置く？</h2>
<p>ここで、Inertia/React構成特有の問題に直面します。</p>
<p>「InertiaはLaravel（PHP）がビューを配信する仕組みだから、<code>npm run build</code> で生成された <code>public/build</code> フォルダは、PHP-FPMコンテナにだけ置けば良いのでは？」</p>
<p>これはよくある誤解ですが、<b>パフォーマンスと責務分離の観点から、ビルドした静的ファイル（JS/CSS）はNginxコンテナに配置するのが正解</b>です。</p>
<p>この理由を理解するために、Inertiaのページが読み込まれる2段階のフローを見てみましょう。</p>
<h3>ステップ1: HTMLシェルのリクエスト (PHP-FPMが処理)</h3>
<ol start="1">
<li>ブラウザが <code>https://example.com/dashboard</code> にアクセスします。</li>
<li>ALB → Nginxコンテナがリクエストを受け取ります。</li>
<li>NginxはこれがPHPのリクエストだと判断し、<code>fastcgi_pass 127.0.0.1:9000</code> を使って<b>PHP-FPMコンテナ</b>に転送します。</li>
<li>PHP (Laravel) が起動し、Inertiaは <code>app.blade.php</code> をレンダリングしようとします。</li>
<li>このとき、PHPは <b><code>public/build/manifest.json</code></b> を読み取り、HTMLに含めるべきJS/CSSのファイル名を特定します。</li>
<li>PHPは以下のようなHTMLの「ガワ」を<b>生成</b>し、Nginx経由でブラウザに返します。
<p><response-element class="" ng-version="0.0.0-PLACEHOLDER"><code-block _nghost-ng-c535465467="" class="ng-tns-c535465467-474 ng-star-inserted"></p>
<div _ngcontent-ng-c535465467="" class="code-block ng-tns-c535465467-474 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" jslog="223238;track:impression,attention;BardVeMetadataKey:[[&quot;r_4fea32b244968dc8&quot;,&quot;c_103da6cb53c10bc2&quot;,null,&quot;rc_8751fdc7f6beda59&quot;,null,null,&quot;ja&quot;,null,1,null,null,1,0]]">
<div _ngcontent-ng-c535465467="" class="formatted-code-block-internal-container ng-tns-c535465467-474">
<div _ngcontent-ng-c535465467="" class="animated-opacity ng-tns-c535465467-474">
<pre _ngcontent-ng-c535465467="" class="ng-tns-c535465467-474"><code _ngcontent-ng-c535465467="" role="text" data-test-id="code-content" class="code-container formatted ng-tns-c535465467-474"><span class="hljs-tag">&lt;<span class="hljs-name">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"/build/assets/app.12345.js"</span> <span class="hljs-attr">defer</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/build/assets/app.67890.css"</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span> <span class="hljs-attr">data-page</span>=<span class="hljs-string">"..."</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
</div>
</div>
</div>
<p></code-block></response-element></li>
</ol>
<h3>ステップ2: 静的アセット(JS/CSS)のリクエスト (Nginxが処理)</h3>
<ol start="1">
<li>ブラウザは、ステップ1で受け取ったHTMLを解析します。</li>
<li>「<code>/build/assets/app.12345.js</code> と <code>/build/assets/app.67890.css</code> が必要だ」と判断し、ブラウザは<b>別途2回のリクエスト</b>をサーバーに送信します。</li>
<li><b>このリクエストは、もはやPHPとは全く関係ありません。</b></li>
<li>ALB → <b>Nginxコンテナ</b>がこの2つのリクエストを受け取ります。</li>
<li>Nginxは「自分の管理下（<code>public</code> フォルダ）にそのファイルがあるか？」を探します。</li>
<li><b>Nginxコンテナに <code>public/build/</code> 以下の実体ファイルが存在するため</b>、NginxはPHP-FPMを起動することなく、それらの静的ファイルを<b>超高速で</b>ブラウザに直接返します。</li>
</ol>
<p>もし、NginxコンテナにJS/CSSファイルがなければ、このリクエストもPHP-FPMに転送されてしまい、「JSファイルを取得するためだけにLaravelを起動する」という深刻なパフォーマンスボトルネックが発生します。</p>
<h3>CI/CDでのベストプラクティス</h3>
<p>上記2ステップから、CI/CDパイプラインでDockerイメージをビルドする際は、以下の構成が最適です。</p>
<ol start="1">
<li><code>npm run build</code> を実行し、<code>public/build</code> ディレクトリを生成します。</li>
<li><b>PHP-FPMイメージのビルド:</b>
<ul>
<li>Laravelのコード（<code>app/</code>, <code>routes/</code> など）をコピーします。</li>
<li>ステップ1のために <b><code>public/build/manifest.json</code></b> をコピーします。（リンク生成に必要）</li>
</ul>
</li>
<li><b>Nginxイメージのビルド:</b>
<ul>
<li><code>nginx.conf</code> をコピーします。</li>
<li>ステップ2のために <b><code>public</code> フォルダ全体</b>（<code>index.php</code> と、<code>public/build</code> 以下の <b>全てのJS/CSSファイル</b> を含む）をコピーします。（静的アセットの配信用）</li>
</ul>
</li>
</ol>
<h3><b>結論</b></h3>
<p><code>manifest.json</code> はPHP-FPMコンテナに、JS/CSSの実体ファイルはNginxコンテナに必要です。</p>
<p>両方のイメージに <code>public</code> フォルダ（または <code>public/build</code>）全体をコピーするのが、最もシンプルで確実な方法です。</p>
<h2>まとめ</h2>
<p>ECS Fargateで Laravel + Inertia + React 構成を動かすための要点をまとめます。</p>
<ol start="1">
<li><b>構成</b>: 「1タスク2コンテナ（Nginx + PHP-FPM）」構成を採用します。</li>
<li><b><code>fastcgi_pass</code></b>: NginxからPHP-FPMへの通信は、同一タスク内のため <code>fastcgi_pass 127.0.0.1:9000;</code> を指定します。</li>
<li><b>静的ファイル</b>: ビルドしたJS/CSSは、パフォーマンス最適化のためNginxコンテナに配置し、Nginxから直接配信させます。</li>
<li><b><code>manifest.json</code></b>: HTMLシェル生成のため、PHP-FPMコンテナにも <code>manifest.json</code> が必要です。</li>
</ol>
<p>この構成により、PHP-FPMは動的処理に専念し、Nginxは静的ファイルの高速配信に専念するという、「1コンテナ1責務」のメリットを最大限に活かした、スケーラブルな本番環境を構築できます。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://otonan-syusyoku.work/archives/2153/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Nginxの「upstream sent too big header」エラーを解決する：LaravelとPHP-FPM環境での対処法</title>
		<link>https://otonan-syusyoku.work/archives/2128</link>
					<comments>https://otonan-syusyoku.work/archives/2128#respond</comments>
		
		<dc:creator><![CDATA[hrokig2]]></dc:creator>
		<pubDate>Mon, 23 Jun 2025 15:04:51 +0000</pubDate>
				<category><![CDATA[インフラ]]></category>
		<category><![CDATA[AWS]]></category>
		<category><![CDATA[LAMP]]></category>
		<category><![CDATA[Laravel]]></category>
		<category><![CDATA[nginx]]></category>
		<category><![CDATA[PHP]]></category>
		<guid isPermaLink="false">https://otonan-syusyoku.work/?p=2128</guid>

					<description><![CDATA[環境 Ubuntu：24.04 PHP：8.2 Laravel：11.x 起こったこと NginxをWebサーバーとして、LaravelアプリケーションとPHP-FPMを連携させている環境で、ある日突然 upstream [&#8230;]]]></description>
										<content:encoded><![CDATA[<h2 data-sourcepos="7:1-7:154">環境</h2>
<p>Ubuntu：24.04<br />
PHP：8.2<br />
Laravel：11.x</p>
<h2 data-sourcepos="5:1-5:183">起こったこと</h2>
<p data-sourcepos="5:1-5:183">NginxをWebサーバーとして、LaravelアプリケーションとPHP-FPMを連携させている環境で、ある日突然 <code>upstream sent too big header while reading response header from upstream</code> というエラーがNginxのエラーログに表示され、Webサイトが正常に表示されなくなリマした…</p>
<p data-sourcepos="7:1-7:154">調べていくと、このエラーは、<span class="sc_marker blue"><strong>PHP-FPM（アップストリームサーバー）がNginxに返そうとしたHTTPレスポンスヘッダーのサイズが、Nginxが受け入れられる設定値の上限を超えてしまった場合に発生</strong></span>します。</p>
<p data-sourcepos="7:1-7:154">特にLaravelのような動的なアプリケーションでは、セッション情報やクッキーの肥大化が原因で発生しやすい問題とのことでやんす。</p>
<h2 data-sourcepos="9:1-9:15">エラーが起きる原因は？</h2>
<p data-sourcepos="11:1-11:182">NginxがWebサーバーとしてクライアントからのリクエストを受け取り、それをバックエンドのPHP-FPM（FastCGIプロセス）に渡して処理してもらいます。</p>
<p data-sourcepos="11:1-11:182">PHP-FPMが処理を終えてNginxに応答を返す際、そのHTTPレスポンスにはヘッダー情報が含まれます。このヘッダー情報がNginxが設定しているバッファのサイズを超えると、エラーが発生してしまいます。</p>
<p data-sourcepos="13:1-13:24">主な原因として、以下のようなものが挙げられます。</p>
<ul data-sourcepos="15:3-18:0">
<li data-sourcepos="15:3-15:177"><strong>セッションデータの肥大化:</strong> Laravelなどのフレームワークでセッションをクッキーベースで管理している場合、ユーザーが多くの情報をセッションに保存したり、ショッピングカートに大量のアイテムを追加したりすると、クッキーのサイズが大きくなります。この大きなクッキー情報がレスポンスヘッダーに含まれることで、上限を超えてしまうことがあります。</li>
<li data-sourcepos="16:3-16:100"><strong>多数のクッキーやカスタムヘッダー:</strong> アプリケーションが非常に多くのクッキーを設定している、または独自のカスタムヘッダーを大量に追加している場合も、ヘッダー全体のサイズが大きくなります。</li>
<li data-sourcepos="17:3-18:0"><strong>リダイレクトループ:</strong> 不適切なリダイレクト設定により、ブラウザが何度もリダイレクトを繰り返す際、そのたびに新しいクッキーやヘッダーが付与され、最終的にヘッダーが大きくなってしまうケースも考えられます。</li>
</ul>
<h2 data-sourcepos="19:1-19:21">解決策：Nginxの設定を調整する</h2>
<p data-sourcepos="21:1-21:48">最も手軽で直接的な解決策は、Nginxが受け入れられるヘッダーバッファのサイズを増やすことです。</p>
<p data-sourcepos="23:1-23:162">Nginxの設定ファイル（通常は <code>/etc/nginx/nginx.conf</code> か、サイト固有の <code>.conf</code> ファイル）を開き、<code>http</code> ブロック内、または対象の <code>server</code> ブロック内に以下のディレクティブを追加または調整します。<code>http</code> ブロックに記述すると、すべてのバーチャルホストに適用されます。</p>
<div _ngcontent-ng-c1213168243="" class="code-block ng-tns-c1213168243-479 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" jslog="223238;track:impression;BardVeMetadataKey:[[&quot;r_7b21b8e99d7c089f&quot;,&quot;c_6fface8cac75276a&quot;,null,&quot;rc_b9532f80be7d9863&quot;,null,null,&quot;ja&quot;,null,1,null,null,1,0]]">
<div _ngcontent-ng-c1213168243="" class="formatted-code-block-internal-container ng-tns-c1213168243-479">
<div _ngcontent-ng-c1213168243="" class="animated-opacity ng-tns-c1213168243-479">
<pre _ngcontent-ng-c1213168243="" class="ng-tns-c1213168243-479"><code _ngcontent-ng-c1213168243="" role="text" data-test-id="code-content" class="code-container formatted ng-tns-c1213168243-479" data-sourcepos="25:1-42:1"><span class="hljs-section">http</span> {
    <span class="hljs-comment"># ... 他の http ブロック内の設定 ...</span>

    <span class="hljs-comment"># upstream sent too big header エラー対策</span>
    <span class="hljs-comment"># PHP-FPM（FastCGI）からのレスポンスヘッダーに対応するため、fastcgi_ のディレクティブを設定します。</span>
    <span class="hljs-comment"># 必要に応じて proxy_ のディレクティブも設定しますが、PHP-FPMの場合はfastcgi_を優先します。</span>
    <span class="hljs-attribute">fastcgi_buffer_size</span> <span class="hljs-number">128k</span>;   <span class="hljs-comment"># FastCGIヘッダーバッファの初期サイズ。デフォルトは4k/8k。</span>
    <span class="hljs-attribute">fastcgi_buffers</span>     <span class="hljs-number">4</span> <span class="hljs-number">256k</span>; <span class="hljs-comment"># 256KBのバッファを4つ用意。合計1MB。</span>

    <span class="hljs-comment"># 必要に応じて、もしプロキシとして使用している場合も考慮するなら、こちらも設定</span>
    <span class="hljs-comment"># proxy_buffer_size   128k;</span>
    <span class="hljs-comment"># proxy_buffers       4 256k;</span>
    <span class="hljs-comment"># proxy_busy_buffers_size 256k;</span>

    <span class="hljs-comment"># ... 他の http ブロック内の設定 ...</span>
}
</code></pre>
</div>
</div>
</div>
<p>&nbsp;</p>
<h3 data-sourcepos="44:1-44:15"><strong>ディレクティブの解説</strong></h3>
<ul data-sourcepos="46:3-48:0">
<li data-sourcepos="46:3-46:150"><code>fastcgi_buffer_size</code>: NginxがFastCGIからのレスポンスヘッダーを読み込むための最初のバッファサイズを指定します。デフォルト値（通常は <code>4k</code> または <code>8k</code>）よりも大きな値（例：<code>128k</code>）を設定することで、大きなヘッダーにも対応できるようになります。</li>
<li data-sourcepos="47:3-48:0"><code>fastcgi_buffers</code>: ヘッダーを含むレスポンス全体をバッファリングするためのバッファの数とサイズを設定します。上記の例では、<code>256KB</code>のバッファを<code>4</code>つ使用し、合計で<code>1MB</code>のバッファスペースを確保します。</li>
</ul>
<p data-sourcepos="49:1-50:107"><strong>注意点:</strong> これらの値を必要以上に大きくしすぎると、Nginxが使用するメモリ量が増加し、サーバーリソースを圧迫する可能性があります。まずはエラーが出なくなる最小限の増加から試し、サーバーの負荷状況を見ながら調整してください。</p>
<h3 data-sourcepos="52:1-52:12">設定変更後の手順</h3>
<ol data-sourcepos="54:1-65:0">
<li data-sourcepos="54:1-54:46"><strong>Nginx設定ファイルを保存:</strong> 変更したNginx設定ファイルを保存します。</li>
<li data-sourcepos="55:1-59:60"><strong>設定ファイルのテスト:</strong> <response-element class="" ng-version="0.0.0-PLACEHOLDER"><code-block _nghost-ng-c1213168243="" class="ng-tns-c1213168243-480 ng-star-inserted">
<div _ngcontent-ng-c1213168243="" class="code-block ng-tns-c1213168243-480 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" jslog="223238;track:impression;BardVeMetadataKey:[[&quot;r_7b21b8e99d7c089f&quot;,&quot;c_6fface8cac75276a&quot;,null,&quot;rc_b9532f80be7d9863&quot;,null,null,&quot;ja&quot;,null,1,null,null,1,0]]">
<div _ngcontent-ng-c1213168243="" class="code-block-decoration header-formatted gds-title-s ng-tns-c1213168243-480 ng-star-inserted"></div>
<div _ngcontent-ng-c1213168243="" class="formatted-code-block-internal-container ng-tns-c1213168243-480">
<div _ngcontent-ng-c1213168243="" class="animated-opacity ng-tns-c1213168243-480">
<pre _ngcontent-ng-c1213168243="" class="ng-tns-c1213168243-480"><code _ngcontent-ng-c1213168243="" role="text" data-test-id="code-content" class="code-container formatted ng-tns-c1213168243-480" data-sourcepos="56:5-58:17">sudo nginx -t</code></pre>
</div>
</div>
</div>
<p>&nbsp;</p>
<p></code-block></response-element>このコマンドで構文エラーがないかを確認します。<code>test is successful</code> と表示されればOKです。</li>
<li data-sourcepos="60:1-65:0"><strong>Nginxのリロード:</strong> <response-element class="" ng-version="0.0.0-PLACEHOLDER"><code-block _nghost-ng-c1213168243="" class="ng-tns-c1213168243-481 ng-star-inserted">
<div _ngcontent-ng-c1213168243="" class="code-block ng-tns-c1213168243-481 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" jslog="223238;track:impression;BardVeMetadataKey:[[&quot;r_7b21b8e99d7c089f&quot;,&quot;c_6fface8cac75276a&quot;,null,&quot;rc_b9532f80be7d9863&quot;,null,null,&quot;ja&quot;,null,1,null,null,1,0]]">
<div _ngcontent-ng-c1213168243="" class="formatted-code-block-internal-container ng-tns-c1213168243-481">
<div _ngcontent-ng-c1213168243="" class="animated-opacity ng-tns-c1213168243-481">
<pre _ngcontent-ng-c1213168243="" class="ng-tns-c1213168243-481"><code _ngcontent-ng-c1213168243="" role="text" data-test-id="code-content" class="code-container formatted ng-tns-c1213168243-481" data-sourcepos="61:5-63:31">sudo systemctl reload nginx
</code></pre>
</div>
</div>
</div>
<p>&nbsp;</p>
<p></code-block></response-element> Nginxサービスをリロードして、新しい設定を適用します。</li>
</ol>
<p data-sourcepos="66:1-66:87">これで、Nginxが大きなHTTPレスポンスヘッダーを適切に処理できるようになり、「<code>upstream sent too big header</code>」エラーは解消されるはずです。</p>
<h2>根本的な解決策：Laravelアプリケーション側の最適化</h2>
<p data-sourcepos="70:1-70:87">Nginxの設定でエラーを抑制できますが、<strong>Laravelアプリケーション側で不必要に大きなヘッダーが生成されている</strong>場合は、そちらの最適化も検討することをお勧めします。</p>
<ul data-sourcepos="72:3-78:0">
<li data-sourcepos="72:3-73:202"><strong>セッションデータの見直し:</strong>
<ul data-sourcepos="73:7-73:202">
<li data-sourcepos="73:7-73:202">クッキーに保存しているセッションデータが大きすぎる場合、データベースやRedisなどの<strong>サーバーサイドストレージにセッションを保存する</strong>ようにLaravelの設定を変更することを検討してください。これにより、クッキーにセッションIDのみが保存されるため、クッキーのサイズを大幅に削減できます。Laravelのセッション設定は <code>config/session.php</code> で変更可能です。</li>
</ul>
</li>
<li data-sourcepos="74:3-75:73"><strong>クッキーの管理:</strong>
<ul data-sourcepos="75:7-75:73">
<li data-sourcepos="75:7-75:73">アプリケーションが設定しているクッキーの数や、個々のクッキーのサイズを減らせないかレビューします。不要なクッキーは削除しましょう。</li>
</ul>
</li>
<li data-sourcepos="76:3-78:0"><strong>カスタムヘッダーの確認:</strong>
<ul data-sourcepos="77:7-78:0">
<li data-sourcepos="77:7-78:0">アプリケーションがレスポンスに含めているカスタムHTTPヘッダーが多すぎないか、または内容が大きすぎないかを確認します。</li>
</ul>
</li>
</ul>
<p data-sourcepos="79:1-79:84">Nginxの設定調整で当面のエラーは解消されますが、アプリケーションの効率性を高めるためにも、Laravel側のヘッダー生成ロジックを見直すことは良いプラクティスです。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://otonan-syusyoku.work/archives/2128/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>AmazonLinux2023はPHP7系が使えません</title>
		<link>https://otonan-syusyoku.work/archives/1845</link>
					<comments>https://otonan-syusyoku.work/archives/1845#respond</comments>
		
		<dc:creator><![CDATA[hrokig2]]></dc:creator>
		<pubDate>Mon, 19 Aug 2024 22:29:45 +0000</pubDate>
				<category><![CDATA[インフラ]]></category>
		<category><![CDATA[仕事の独り言]]></category>
		<category><![CDATA[AWS]]></category>
		<category><![CDATA[EC2]]></category>
		<category><![CDATA[LAMP]]></category>
		<category><![CDATA[PHP]]></category>
		<guid isPermaLink="false">https://otonan-syusyoku.work/?p=1845</guid>

					<description><![CDATA[とある日、サーバー移行のタスクが一端のプログラマーである私に降ってきました。 セキュリティサポートが2022年末に終了して久しいPHP7系で動いているシステムが乗っかているサーバーの移行タスクです。 &#160; 事の発 [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>とある日、サーバー移行のタスクが一端のプログラマーである私に降ってきました。</p>
<p>セキュリティサポートが2022年末に終了して久しいPHP7系で動いているシステムが乗っかているサーバーの移行タスクです。</p>
<p>&nbsp;</p>
<h2>事の発端</h2>
<p><span>CentOS 7のサポート終了がきっかけです。</span></p>
<p>DevOps系のお仕事に携わっている人なら理解してくれると思うのですが、サポートが切れた技術を継続して使用していくことの恐ろしさったら中々のものです。</p>
<p>まぁそんな事情があったので、CentOSを別のOSに移行していこうー！というノリで始まりました。</p>
<h2>困ったことに</h2>
<p>クラウドサービスはAWSを選択しているので、AWSが提供しているOSであるAmazonLinux2023を移行先のOSとして決定しました。</p>
<p>単純なLAMP環境を構築することが今回のタスクだったので、作業をガシガシ進めていくと一つの問題にぶち当たりました。</p>
<p><strong>PHP7系がインストールできない…</strong></p>
<h3>Remiリポジトリ, EPELが使えない</h3>
<p>そもそもの問題としてPHP7系は2022年末にサポートが切れているので、AmazonLinuxがサポートしている訳がありません。<br />
普通に考えてみれば至極真っ当です。</p>
<p>今回のタスクでは、PHP7系を使うことは外せない要件として上がっていたので、どうしてもPHP7系が必要です。</p>
<p>こうなったらRemiリポジトリから持ってくるしかないなーと思っていた矢先、Remiリポジトリも使えないとのこと。（EPELがそもそも使えないため</p>
<p><a href="https://docs.aws.amazon.com/ja_jp/linux/al2023/ug/relationship-to-fedora.html">AWS ドキュメント</a><br />
<a href="https://docs.aws.amazon.com/ja_jp/linux/al2023/ug/compare-with-al2.html#epel">EPEL</a><br />
<a href="https://qiita.com/charon/items/6d34ae798e9b05e8bd0a">CentOSなどで使う、RemiRepositoryってなんだ？</a></p>
<p>&nbsp;</p>
<p>今回の要件（PHP7系 install）を達成する事は、Amazonlinux2023 では不可能だと知った瞬間でした…</p>
<h2>OS選定</h2>
<p>CentOS の移行先で検索すると以下のOSたちがよくヒットしました。</p>
<ul>
<li>RedHatEnterpriseLinux</li>
<li>AlmaLinux</li>
<li>RockyLinux</li>
</ul>
<p><strong>PHP7系 が使えること</strong> 及び <strong>互換性があること , </strong><strong>ある程度の認知度 </strong>があるOSを選択したかったので、 RockyLinux で行くことに決定しました。</p>
<p>最終的にはRockyLinux上でRemiリポジトリを使用して目的の環境を作成することができたよーん。</p>
<p>&nbsp;</p>
<h2>感謝を捧げる</h2>
<p>振り回された感が拭えないタスクだったのですが、誰が悪いとかはないなーという所感です。</p>
<p>CentOSのサポートが切れるのも、Amazonlinux2023 がPHP7 をサポートしていないのも、PHP7を使って未だに運用しているシステムも誰も悪くないです。<br />
（PHP7に゙関しては早くバージョンアップしてほしい…</p>
<p>最終的には何とかなったので、 RockeyLinux のコントリビューターに感謝を捧げます</p>
<p>&nbsp;</p>
]]></content:encoded>
					
					<wfw:commentRss>https://otonan-syusyoku.work/archives/1845/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>意外と見落とされがち！PHP.iniの重要な設定ポイント</title>
		<link>https://otonan-syusyoku.work/archives/1753</link>
					<comments>https://otonan-syusyoku.work/archives/1753#respond</comments>
		
		<dc:creator><![CDATA[hrokig2]]></dc:creator>
		<pubDate>Sun, 12 May 2024 06:13:53 +0000</pubDate>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Tips]]></category>
		<guid isPermaLink="false">https://otonan-syusyoku.work/?p=1753</guid>

					<description><![CDATA[PHPを扱う際、多くの開発者がphp.iniの設定を最適化することの重要性を見落としがちです。このファイルはPHPの動作をカスタマイズし、セキュリティを強化するためのキーとなります。今回の記事では、意外とできていないph [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>PHPを扱う際、多くの開発者がphp.iniの設定を最適化することの重要性を見落としがちです。このファイルはPHPの動作をカスタマイズし、セキュリティを強化するためのキーとなります。今回の記事では、意外とできていないphp.iniの設定に焦点を当て、その設定方法とそれぞれの設定がもたらす影響について解説します。</p>
<h2>メモリ制限の設定 (memori_limit)</h2>
<p>PHPスクリプトが消費することができる最大メモリ量を指定します。デフォルト設定では「128M」が多いですが、大規模なアプリケーションを扱う場合はこの値を増やす必要があります。不足するとスクリプトが突然停止することがあるため、適切なメモリ量を設定しましょう。<span class="" data-state="closed"></span></p>
<pre class="line-numbers"><code class="!whitespace-pre hljs language-ini"><span class="hljs-attr">memory_limit</span> = <span class="hljs-number">256</span>M</code></pre>
<h2>実行時間の制限 (max_execution_time)</h2>
<p>スクリプトが実行を終了するまでの最大時間を秒単位で設定します。デフォルトは「30秒」ですが、時間がかかるプロセスを扱う場合はこの値を調整するこことが重要です。</p>
<pre class="line-numbers"><code class="language-php">max_execution_time = 60</code></pre>
<h2>エラーレポーティング (error_reporting)</h2>
<p>開発中はすべてのエラーを表示することが重要です。しかし、本番環境ではエラー表示を抑えるべきです。この設定を通じて、どのレベルのエラーをレポートするかを制御できます。</p>
<pre class="line-numbers"><code class="language-php">error_reporting = E_ALL &amp; ~E_DEPRECATED &amp; ~E_STRICT</code></pre>
<h2>ファイルアップロード (file_uploads)</h2>
<p>ファイルアップロードを許可するかどうかを制御します。セキュリティ上の理由から、使用しない場合は無効化することが推奨されます。</p>
<pre class="line-numbers"><code class="language-php">file_uploads = On</code></pre>
<h2>セッションのセキュリティ設定</h2>
<p>セッションIDの扱いには特に注意が必要です。セッションハイジャックを防ぐために、セッションIDの再生成やセキュアなクッキーの使用が推奨されます。</p>
<pre class="line-numbers">session.use_strict_mode = 1
session.cookie_secure = 1
session.cookie_httponly = 1</pre>
<h2>まとめ</h2>
<p>これらの設定は、PHPのパフォーマンスとセキュリティを大きく向上させることができます。php.iniの設定は一見複雑に見えるかもしれませんが、それぞれの項目に慎重に注意を払い、アプリケーションのニーズに応じて適切に調整することが非常に重要です。この記事があなたのPHP設定を見直すきっかけになれば幸いです。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://otonan-syusyoku.work/archives/1753/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>RESTful APIとSPAの関係についてReactとPHPで解説します</title>
		<link>https://otonan-syusyoku.work/archives/1674</link>
					<comments>https://otonan-syusyoku.work/archives/1674#respond</comments>
		
		<dc:creator><![CDATA[hrokig2]]></dc:creator>
		<pubDate>Sun, 25 Feb 2024 12:40:20 +0000</pubDate>
				<category><![CDATA[好きではないJS]]></category>
		<category><![CDATA[絶対に必要なIT基礎知識]]></category>
		<category><![CDATA[HTTP]]></category>
		<category><![CDATA[JS]]></category>
		<category><![CDATA[Laravel]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[React]]></category>
		<category><![CDATA[Vue]]></category>
		<category><![CDATA[セキュリティ]]></category>
		<guid isPermaLink="false">https://otonan-syusyoku.work/?p=1674</guid>

					<description><![CDATA[RESTful APIとは？ RESTful APIは、ウェブアプリケーションやウェブサービスで使われるプログラミングインターフェースです。 REST（Representational State Transfer）の原 [&#8230;]]]></description>
										<content:encoded><![CDATA[<h2>RESTful APIとは？</h2>
<p><img fetchpriority="high" decoding="async" src="https://otonan-syusyoku.work/wp-content/uploads/2024/02/DMARCを設定しなくちゃ-1.png" alt="What&#96;s up" width="1000" height="500" class="aligncenter size-full wp-image-1665" srcset="https://otonan-syusyoku.work/wp-content/uploads/2024/02/DMARCを設定しなくちゃ-1.png 1000w, https://otonan-syusyoku.work/wp-content/uploads/2024/02/DMARCを設定しなくちゃ-1-300x150.png 300w, https://otonan-syusyoku.work/wp-content/uploads/2024/02/DMARCを設定しなくちゃ-1-768x384.png 768w" sizes="(max-width: 1000px) 100vw, 1000px" /></p>
<p>RESTful APIは、ウェブアプリケーションやウェブサービスで使われるプログラミングインターフェースです。<br />
REST（Representational State Transfer）の原則に基づいて設計されており、ウェブ上のリソースへのアクセスや操作を統一的な方法で提供します。<br />
リソースはURLで識別され、HTTPメソッド（GET、POST、PUT、DELETEなど）を使ってアクセスされます。</p>
<p>[getpost id=&#8221;1672&#8243; cat_name=&#8221;1&#8243; date=&#8221;0&#8243;]</p>
<h2>SPA（シングルページアプリケーション）とは？</h2>
<p>SPAは、ユーザーがアプリケーションを操作する際にページの再読み込みをせずに、必要なデータのみをサーバーから非同期に取得し、動的にページの内容を更新するウェブアプリケーションの形式です。</p>
<p>これにより、<span class="sc_marker blue"><strong>従来のマルチページアプリケーションに比べてユーザーエクスペリエンスが向上します。</strong></span></p>
<p>僕が所属しているチームでも<strong>ページのリロードが嫌だというユーザーの使用感からSPAへの移行が少しづつ始まってきています</strong>。<br />
世間的にもSPAは流行っていますよね。（流行っているから偉いというわけではない。知ることが大事</p>
<h2>RESTful APIとSPAの関係性</h2>
<p><img decoding="async" src="https://otonan-syusyoku.work/wp-content/uploads/2024/02/Lets-GO.png" alt="Let&#96;s GO" width="1000" height="500" class="aligncenter size-full wp-image-1680" srcset="https://otonan-syusyoku.work/wp-content/uploads/2024/02/Lets-GO.png 1000w, https://otonan-syusyoku.work/wp-content/uploads/2024/02/Lets-GO-300x150.png 300w, https://otonan-syusyoku.work/wp-content/uploads/2024/02/Lets-GO-768x384.png 768w" sizes="(max-width: 1000px) 100vw, 1000px" /></p>
<p>SPAでは、<strong>ページの初回読み込み時にアプリケーションの全てのコード（HTML、CSS、JavaScript）がロード</strong>されます。</p>
<p>その後の<span class="sc_marker blue"><strong>ユーザーの操作によってデータが必要になる場合、SPAはRESTful APIを通じてサーバーからデータを非同期に取得</strong></span>します。<br />
<strong>このデータはJSONやXML形式で返され、JavaScriptを用いてクライアントサイドで処理され、ページの一部分だけが動的に更新</strong>されます。</p>
<p>このアーキテクチャにより、SPAはユーザーにスムーズで反応の良いインターフェイスを提供できます。</p>
<p><span class="sc_marker blue"><strong>RESTful APIはこのプロセスの背後にあるデータ通信の基盤となります。</strong></span></p>
<h2>PHPとReactを使った例</h2>
<p>ここでは、PHPで書かれた簡単なRESTful APIと、Reactを使ったSPAの例を示します。</p>
<h3>PHPによるRESTful API</h3>
<p>PHPで簡単なAPIエンドポイントを作成します。この例では、GETリクエストを受け取り、簡単なJSONデータを返します。</p>
<div class="dark bg-gray-950 rounded-md">
<div>
<pre class="line-numbers"><code class="language-php">&lt;?php 
    header('Content-Type: application/json');
    $response = ['message' =&gt; 'Hello, world!'];
    echo json_encode($response); 

<code>    // Response
    //{"message": "Hello, world!"}</code> ?&gt;</code></pre>
</div>
</div>
<p>このPHPスクリプトは、サーバーに配置してのようにアクセスすると<span style="background-color: #ffffff; color: #333333; font-family: 游ゴシック, YuGothic, 'ヒラギノ角ゴ Pro W3', 'Hiragino Kaku Gothic Pro', Verdana, メイリオ, Meiryo, Osaka, 'ＭＳ Ｐゴシック', 'MS PGothic', sans-serif;">JSONが返されます。</span></p>
<h3>ReactによるSPA</h3>
<p>Reactで簡単なSPAを作成し、上記のAPIからデータを取得して表示します。</p>
<div class="dark bg-gray-950 rounded-md">
<div class="p-4 overflow-y-auto">
<pre class="line-numbers"><code class="language-js">import React, { useState, useEffect } from 'react';
import axios from 'axios';

function App() {
  const [message, setMessage] = useState('');

  useEffect(() =&gt; {
    axios.get('http://yourserver.com/index.php')
      .then(response =&gt; {
        setMessage(response.data.message);
      })
      .catch(error =&gt; console.error('There was an error!', error));
  }, []);

  return (
    &lt;div&gt;
      &lt;button&gt;{message}&lt;/button&gt;
    &lt;/div&gt;
  );
}

export default App;
</code></pre>
<p>このコードは、コンポーネントがマウントされた後にAPIからデータを取得し、取得したメッセージを表示します。<br />
React（クライアントサイド）とPHP（サーバーサイド）を使った、RESTful APIとSPAの基本的な連携を示しています。</p>
</div>
<p>今回のコードでは、データを取得して表示するだけのコードになっていますが、Event処理などによりリロードされることなく動的にページの内容を変更させることが可能です。</p>
<h2>セキュリティとパフォーマンスの考慮</h2>
</div>
<p>SPA（シングルページアプリケーション）とRESTful APIを使用する際には、セキュリティとパフォーマンスの両方に配慮することが重要です。<br />
以下では、これらの観点から基本的なガイドラインをいくつか紹介します。</p>
<h3>セキュリティ</h3>
<ol>
<li><strong>HTTPSを使用する<br />
</strong>データの暗号化を確実に行うため、APIとの通信には常にHTTPSを使用します。（当たり前です）<br />
これにより、中間者攻撃（MITM）のリスクを減らすことができます。</li>
<li><strong>CORSポリシーを適切に設定する<br />
</strong>クロスオリジンリソースシェアリング（CORS）ポリシーを適切に設定し、信頼できるオリジンからのリクエストのみを許可します。<br />
不適切なCORS設定は、セキュリティリスクを招く可能性があります。</li>
<li><strong>認証と認可<br />
</strong>JWT（JSON Web Tokens）やOAuthなどの堅牢な認証メカニズムを使用して、APIへのアクセスをセキュアに管理します。<br />
また、ユーザーの権限に基づいてアクセスを制御する認可も重要です。</li>
<li><strong>入力の検証とサニタイズ<br />
</strong> SQLインジェクションやクロスサイトスクリプティング（XSS）攻撃を防ぐため、サーバー側とクライアント側の両方で入力値の検証とサニタイズを行います。</li>
<li><strong>依存関係のセキュリティ<br />
</strong>使用するライブラリやフレームワークのセキュリティ脆弱性に注意し、定期的に更新を行い脆弱性を修正します。</li>
</ol>
<p>この辺の対策については過去に記事にしていますので時間ある人は読んでみてくださーい</p>
<p>[getpost id=&#8221;1618&#8243; cat_name=&#8221;1&#8243; date=&#8221;0&#8243;][getpost id=&#8221;1256&#8243; cat_name=&#8221;1&#8243; date=&#8221;0&#8243;]</p>
<h3>パフォーマンス</h3>
<ol>
<li><strong>コードの分割<br />
</strong>ユーザーが必要とするコードのみをダウンロードするように、コード分割（Code Splitting）を実装します。<br />
これにより、初期ロード時間を短縮できます。</li>
<li><strong>キャッシュ戦略の利用<br />
</strong>静的リソースやAPIレスポンスのキャッシュを適切に利用することで、パフォーマンスを向上させることができます。<br />
ブラウザのキャッシュやCDNの利用が有効です。</li>
<li><strong>遅延ローディング（Lazy Loading）<br />
</strong> 画像やコンポーネントなど、必要になるまでロードしないようにします。<br />
これにより、初期ロードのパフォーマンスを向上させることができます。</li>
<li><strong>APIの最適化<br />
</strong>不必要なデータの送受信を避けるため、APIのレスポンスをできるだけ小さく保ちます。<br />
また、必要なデータのみを取得するためのフィルタリングやページネーションをAPI側でサポートします。</li>
<li><strong>フロントエンドのパフォーマンス最適化<br />
</strong>Reactなどのフロントエンドフレームワークを使用する場合は、不要な再レンダリングを避け、メモ化（memoization）や仮想DOMの効率的な利用などのテクニックを活用します。</li>
</ol>
<p>セキュリティとパフォーマンスは、アプリケーションの設計と開発の初期段階から考慮する必要があります。<br />
これらの基本的なガイドラインを実践することで、ユーザーに安全で快適な体験を提供することができます。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://otonan-syusyoku.work/archives/1674/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>CORSがわからないからちゃんと勉強してみた</title>
		<link>https://otonan-syusyoku.work/archives/1618</link>
					<comments>https://otonan-syusyoku.work/archives/1618#respond</comments>
		
		<dc:creator><![CDATA[hrokig2]]></dc:creator>
		<pubDate>Sun, 28 Jan 2024 06:53:06 +0000</pubDate>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[HTTP]]></category>
		<category><![CDATA[Laravel]]></category>
		<category><![CDATA[MDN]]></category>
		<guid isPermaLink="false">https://otonan-syusyoku.work/?p=1618</guid>

					<description><![CDATA[こんにちは！今日は、Web開発でよく聞くけど意外と理解しづらいCORS（Cross-Origin Resource Sharing）について、一緒に学んでいこうと思います。 Webの世界は複雑で、ちょっとしたことが大きな [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>こんにちは！今日は、Web開発でよく聞くけど意外と理解しづらい<strong>CORS（Cross-Origin Resource Sharing）</strong>について、一緒に学んでいこうと思います。</p>
<p>Webの世界は複雑で、ちょっとしたことが大きな違いを生むんですよね。CORSもその一つ。</p>
<p>さあ、一緒にCORSを理解してきましょう〜</p>
<h2>CORSって何？</h2>
<p>まず、CORSって何？って話から始めます。</p>
<p>CORSは、Webページが異なるオリジン（つまり、異なるドメイン、プロトコル、またはポート）のリソースにアクセスする際のセキュリティメカニズムです。</p>
<p>つまり、あなたのサイトが他のサイトのデータを安全に扱うためのルールのこと。これがないと、セキュリティリスクが高まります。</p>
<table style="border-collapse: collapse; width: 100%;">
<tbody>
<tr style="background-color: #7ab1f0;">
<td style="width: 50%;"></td>
<td style="width: 50%;"></td>
</tr>
<tr>
<td style="width: 50%;">ホスト (Host)</td>
<td style="width: 50%;">インターネット上で情報やリソースを提供するコンピューターやサーバー。各ホストには独自のIPアドレスがあり、インターネット上で特定の位置を持つ。</p>
<p>例：ウェブサイトをホストするサーバー</td>
</tr>
<tr>
<td style="width: 50%;">ドメイン (Domain)</td>
<td style="width: 50%;">インターネット上のアドレスの一部。ウェブサイトや電子メールアドレスを識別するために使われる。人間が理解しやすい形式でサーバーを識別するために用いられる。</p>
<p>例：「example.com」</td>
</tr>
<tr>
<td style="width: 50%;">オリジン (Origin)</td>
<td style="width: 50%;">ウェブページの起源を示す概念。スキーム（プロトコル）、ホスト（ドメイン名）、ポートの組み合わせで定義される。</p>
<p>例：「https://example.com:80」</td>
</tr>
</tbody>
</table>
<h2>なぜCORSが必要なの？</h2>
<p>Webはオープンでつながっているけど、同時にセキュリティも重要です。</p>
<p>例えば、あなたがログインしているサイトAから、悪意のあるサイトBがデータを盗もうとするといったリスクがあります。</p>
<p>CORSは、このような不正なアクセスを防ぐために存在しているんです。<br />
安全なWebのためには、必要不可欠なんですよ。</p>
<h2>CORSの仕組み</h2>
<p>CORSの基本は<span class="sc_marker blue"><strong>「信頼できるオリジンからのリクエストのみ許可する」</strong></span>ということ。</p>
<p>これは、サーバー側で設定され、ブラウザがこの設定に従います。</p>
<p>サーバーは、リクエストが来たときに「このオリジンからのリクエストはOKかな？」とチェック。</p>
<p>OKならリソースを提供し、ダメなら拒否。これで、安全にデータのやりとりができるようになります。</p>
<h3>簡単なCORSの例</h3>
<p>例えば、あなたのサイト（example.com）が、他のサイト（api.example.com）からデータを取得したい場合があるとします。</p>
<p>もし、api.example.comがCORSでexample.comを許可していれば、問題なくデータのやりとりができます。</p>
<p>でも、許可されていなければ、「CORSエラー」が発生してアクセスできないんです。</p>
<h3>CORSをどう扱う？</h3>
<p>開発者としては、CORSは頭痛の種になることも。</p>
<p>しかし、適切に設定することでセキュリティを確保しつつ、必要なデータ交換も可能になります。サーバーの設定を見直したり、必要に応じてオリジンを許可リストに追加することが大切です。</p>
<h2>設定方法</h2>
<h3>素のPHP</h3>
<p>※WEBサーバーにて設定することも可能ですが、今回はPHPでの実装を想定。</p>
<pre class="line-numbers"><code class="language-php">header("Access-Control-Allow-Origin: http://example.com");
header("Access-Control-Allow-Methods: GET, POST, PUT, DELETE");
header("Access-Control-Allow-Headers: Content-Type, X-Requested-With");

/**
* - http://example.com からのアクセスを許可
* - 許可メソッドは GET, POST, PUT, DELETE
* - <span>リクエスト中に許可されるHTTPヘッダー
*     - Content-Type（リソースのメディアタイプ
*     - X-Requested-With（通常、Ajaxリクエストを識別するために使用される）
*/</span></code></pre>
<h3>Laravel</h3>
<p>※<span>Laravel 7.x以降ではデフォルトでCORS設定が組み込まれているっぽい。</span></p>
<pre class="line-numbers"><code class="language-php">return [
    'paths' =&gt; ['api/*'],
    'allowed_methods' =&gt; ['*'],
    'allowed_origins' =&gt; ['http://example.com'],
    'allowed_origins_patterns' =&gt; [],
    'allowed_headers' =&gt; ['*'],
    'exposed_headers' =&gt; [],
    'max_age' =&gt; 0,
    'supports_credentials' =&gt; false,

    /**
     * - http://example.com からのアクセスを許可
     *   'allowed_origins' =&gt; ['http://example.com']
     *   この設定は、http://example.com からのリクエストを許可します。
     * 
     * - 許可されるHTTPメソッド
     *   'allowed_methods' =&gt; ['*']
     *   この設定は、すべてのHTTPメソッドを許可します。特定のメソッドのみを許可する場合は、
     *   ['GET', 'POST', 'PUT', 'DELETE'] のように配列を編集します。
     * 
     * - 許可されるHTTPヘッダー
     *   'allowed_headers' =&gt; ['*']
     *   この設定は、すべてのHTTPヘッダーを許可します。特定のヘッダーのみを許可する場合は、
     *   必要なヘッダーを配列に追加します。
     */
];
</code></pre>
<p>&nbsp;</p>
<h2>まとめ</h2>
<p>CORSは、Webの安全性を保つための重要なメカニズム。最初は複雑に感じるかもしれませんが、基本を理解すれば、より安全で効果的なWebアプリケーション開発が可能になります。</p>
<p>これにてCORSの基本を何となく理解！</p>
]]></content:encoded>
					
					<wfw:commentRss>https://otonan-syusyoku.work/archives/1618/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>自分で作ったサービスで売上を上げるのめちゃくちゃ難しいやんけ</title>
		<link>https://otonan-syusyoku.work/archives/1613</link>
					<comments>https://otonan-syusyoku.work/archives/1613#respond</comments>
		
		<dc:creator><![CDATA[hrokig2]]></dc:creator>
		<pubDate>Sat, 27 Jan 2024 01:55:46 +0000</pubDate>
				<category><![CDATA[仕事の独り言]]></category>
		<category><![CDATA[日々の独り言]]></category>
		<category><![CDATA[生涯独学]]></category>
		<category><![CDATA[AWS]]></category>
		<category><![CDATA[Docker]]></category>
		<category><![CDATA[LAMP]]></category>
		<category><![CDATA[Laravel]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[個人開発]]></category>
		<guid isPermaLink="false">https://otonan-syusyoku.work/?p=1613</guid>

					<description><![CDATA[こんにちは。都内でPHPerとして勤務している三流プログラマーです。 本業とは別に取り組んでいた個人開発でマネタイズができず、サービスを終了させてしまったのでここで供養させてください。 RIP My Service&#8 [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>こんにちは。都内でPHPerとして勤務している三流プログラマーです。</p>
<p>本業とは別に取り組んでいた個人開発でマネタイズができず、サービスを終了させてしまったのでここで供養させてください。</p>
<p>RIP My Service&#8230;</p>
<h2>作ったもの</h2>
<p>HP 兼 予約サイト 兼 ECサイトなるシステムを開発しました。</p>
<p>僕が沖縄県出身ということもあり、沖縄の個人経営を営んでいる小さな企業さんを相手に営業の肩代わりをすることを目的にシステムを開発しました。</p>
<p>というのも沖縄県は観光地として有名でありながらもHPすら持たず、地元の口コミのみで経営を行っているお店が多々ある状態でした。</p>
<p>せっかくの観光地でありながらお客様を呼び込めないのはかなりもったいないという思いから作った次第でございます。</p>
<h2>技術構成</h2>
<table style="border-collapse: collapse; width: 100%;">
<tbody>
<tr>
<td style="width: 50%;">デザイン</td>
<td style="width: 50%;">Tailwind + DaisyUI</td>
</tr>
<tr>
<td style="width: 50%;">フロントエンド</td>
<td style="width: 50%;">React</td>
</tr>
<tr>
<td style="width: 50%;">バックエンド</td>
<td style="width: 50%;">Laravel10</td>
</tr>
<tr>
<td style="width: 50%;">DB</td>
<td style="width: 50%;">RDS(MySQL8.x)</td>
</tr>
<tr>
<td style="width: 50%;">決済</td>
<td style="width: 50%;">Stripe</td>
</tr>
<tr>
<td style="width: 50%;">インフラ</td>
<td style="width: 50%;">サーバー：EC2<br />
DB：RDS<br />
ファイルサーバー：S3</td>
</tr>
<tr>
<td style="width: 50%;">その他</td>
<td style="width: 50%;">Docker</td>
</tr>
</tbody>
</table>
<h2>マネタイズポイント</h2>
<h3>物販購入</h3>
<p>沖縄ならではの商品が展開されるEC機能を開発しました。</p>
<p>1商品当たり個別に x％ の手数料をいただく契約になります。</p>
<h3>予約</h3>
<p>美容院やレジャー施設の予約を受け付ける機能を開発しました。</p>
<p>こちらもEC同様に1予約あたり、x円の手数料 or オプション契約 のどちらかを契約する想定でした。</p>
<h2>売り方</h2>
<p>沖縄出身ではあるものの都内で勤務しているということもあり現地での営業活動ができないという状況でした。<br />
そのため、現地で営業活動を行ってもらうために地元の友だちに「俺はこれから月100万円稼ぐシステムを開発するから営業をしてくれ」と<strong>大きく誇張した言い分</strong>を持って営業活動をしてもらいました。</p>
<p>その友人は面白そうだからという理由で快く引き受けてくれ、以下のマーケティング手法で営業を行ってもらいました。</p>
<ul>
<li>SNS
<ul>
<li>Instagram</li>
<li>Twitter</li>
</ul>
</li>
<li>ブログ</li>
</ul>
<p>先に結論から入るのですが、期間としては6ヶ月間行ってもらい、皆さんの想像通り契約は1件も入らなかったです。</p>
<h2>なぜ売れなかったのか</h2>
<h3>知名度</h3>
<p>これは誰しもが考えることだと思うのですが <span class="sc_marker blue"><strong>認知されていないサービスにお金を払う</strong></span> ことは避けたいはずです。</p>
<p>尚更、登記もしていない<span class="sc_marker blue"><strong>個人が開発したシステムに対して自分の命であるお金を注ぐことはあり得ない</strong></span>です。</p>
<p>考えられる手法としては以下の手法が取れるはずです。</p>
<ul>
<li>正しい営業をする</li>
<li>受託等で知名度を得る</li>
<li>その他のサービスで知名度を得る（知名度が無いことによる無限ループが発生する…）</li>
</ul>
<p>お客様が安心してこのサービスにはお金を投資できると胸を張って言えるようなシステムを構築するのは難しいですね。。。</p>
<h3>効果を得られるか不安</h3>
<p>知名度がないという理由にも近しいですが、当システムを使用することで利益を得られることに対する不安を拭い去る事はできませんでした。</p>
<p>なぜなら<span class="sc_marker blue"><strong>契約が1件もないということは実績が無いということですからお客様からすると不安しかありません</strong></span>。</p>
<p>世の中のスタートアップはどうやって利益を得ているんですか？？</p>
<h3>県民性</h3>
<p>こちらは確固たるデータがある訳ではないんですが、ハードに仕事をしないという県民性を見誤っていた可能性がある事を感じています。</p>
<p>沖縄は「のんかーな性格（のんきな性格）」と呼ばれる人が多い印象です。</p>
<p>事実として僕の知っている<span class="sc_marker blue"><strong>沖縄の経営者では食べていける分を稼げて楽しい毎日を送ることが大切</strong></span>という人が多いです。（4人だけしか知りませんが…）</p>
<p>そういった考えを持っている人に対して業務を効率化して売上を伸ばしましょうなんて言っても「忙しくなるなら現状のままで」といった答えが返ってくるに決まっています。</p>
<p>そのあたりをしっかりと調査する or ターゲットを先に見つける事が大切かなぁという印象です。</p>
<h2>反省</h2>
<p>僕はゴミを作ってしましました。</p>
<p>誰にも使われないシステムなんてゴミです。ただのゴミクズです。</p>
<p>今回は僕一人と頭の悪い友人の時間だけが溶けただけで済んで本当に良かったです。（実際には5万ほど運用費としてお金を使いました。）</p>
<p>仮に起業という形でサービスを開発していた場合は廃業に大きく近づいていたので、個人開発の一環でここまでの勉強ができたのは僕の才能かもしれません。笑</p>
<p>まだまだ作りたいものはあるので引き続き頑張っていきたいですねぇ〜</p>
]]></content:encoded>
					
					<wfw:commentRss>https://otonan-syusyoku.work/archives/1613/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
