Content-Type を一言でいうと
Content-Type は
「このボディの中身は“何の種類のデータ”かを相手に教えるためのヘッダー」
です。
中身が JSON なのか、HTML なのか、画像なのか、テキストなのか。
それをきちんと宣言することで、ブラウザやクライアント、サーバーが
「どうパースするか」「どう扱うか」を正しく判断できます。
HTTP でボディを使うなら、Content-Type を意識できるかどうかが“初級卒業ライン”だと思っていいです。
Content-Type の基本構造
形は「メディアタイプ+オプション」
Content-Type は、だいたい次のような形をしています。
Content-Type: 種類/サブタイプ; オプション...
例えば、よく見るのはこういうものです。
Content-Type: application/jsonContent-Type: text/html; charset=UTF-8Content-Type: image/png
「種類/サブタイプ」の部分を「メディアタイプ(MIME タイプ)」と呼びます。text, image, application などが大きな分類で、その後ろに細かい種類が続くイメージです。
代表的な Content-Type とその意味
JSON を送る・返すとき
REST API で一番よく使うのがこれです。
Content-Type: application/json
意味は「ボディの中身は JSON ですよ」です。
リクエスト側で JSON を送るとき。
POST /api/users HTTP/1.1
Content-Type: application/json
{"name":"Alice","email":"alice@example.com"}
HTTPレスポンス側で JSON を返すとき。
HTTP/1.1 200 OK
Content-Type: application/json
{"id":1,"name":"Alice","email":"alice@example.com"}
HTTPJava(Spring)では、
リクエストを受けるときは @RequestBody と DTO、
レスポンスを返すときはオブジェクトの返り値、
と組み合わせることで、
この application/json を前提にしたやり取りが自然にできます。
HTML を返すとき
普通の Web ページを返すときは、こうなります。
Content-Type: text/html; charset=UTF-8
意味は「HTML で、文字コードは UTF-8 ですよ」です。
ブラウザはこれを見て、HTML としてレンダリングし、文字化けしないように解釈します。
プレーンテキストや画像など
プレーンなテキストなら
Content-Type: text/plain; charset=UTF-8
PNG 画像なら
Content-Type: image/png
JPEG なら
Content-Type: image/jpeg
というように、
「何の種類のデータか」を Content-Type で宣言します。
なぜ Content-Type がそんなに重要なのか
相手が「どう解釈するか」を決める材料だから
同じバイト列でも、
「JSON として読む」のか
「HTML として読む」のか
「画像として読む」のか
で意味がまったく変わります。
Content-Type が正しく付いていれば、
クライアントやサーバーは迷わず正しいパーサーを選べます。
逆に、Content-Type が間違っていたり、付いていなかったりすると、
JSON なのにテキストとして扱われてパースに失敗する
HTML なのに JSON として扱おうとしてエラーになる
といったトラブルが起きます。
セキュリティにも関わる
ブラウザは Content-Type を見て、
「これはスクリプトとして実行していいのか」
「ただのファイルとしてダウンロードすべきか」
などを判断します。
例えば、本来はただのテキストなのにtext/html として返してしまうと、
ブラウザが HTML として解釈し、
意図しないスクリプトが動く可能性があります。
だから、
「中身と Content-Type を正しく対応させる」ことは、
単なるマナーではなく、セキュリティ上も大事なポイントです。
Java(Spring)での Content-Type の扱い
リクエスト側:何を送るかを宣言する
クライアントとして API を叩くとき、
JSON を送りたいなら必ず Content-Type: application/json を付けます。
Spring の RestTemplate や WebClient を使うときも、
ヘッダーに Content-Type を設定してからボディを送る、という形になります。
例えば WebClient なら、こんなイメージです。
webClient.post()
.uri("/api/users")
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(new UserRequest("Alice", "alice@example.com"))
.retrieve()
.bodyToMono(UserResponse.class);
Javaここで contentType(MediaType.APPLICATION_JSON) が、
HTTP の Content-Type: application/json に対応します。
レスポンス側:何を返すかを宣言する
Spring MVC では、@RestController + オブジェクト返り値
というだけで、基本的には application/json が付きます。
HTML を返したい場合は、
テンプレートエンジン(Thymeleaf など)を使ったり、@Controller でビュー名を返したりすると、text/html が付くように設定できます。
また、明示的に指定したいときは、
@GetMapping(value = "/text", produces = "text/plain;charset=UTF-8")
public String text() {
return "hello";
}
Javaのように produces 属性で指定することもできます。
Content-Type と Accept の関係
「送るときの宣言」と「欲しいときの希望」
Content-Type:
「今送っているボディは、こういう種類のデータですよ」
Accept:
「レスポンスは、この種類のデータで返してほしいです」
という関係です。
例えば、クライアントが
GET /api/users/1 HTTP/1.1
Accept: application/json
HTTPと送ると、
「JSON で返してほしい」という希望になります。
サーバーはそれに応じて
HTTP/1.1 200 OK
Content-Type: application/json
{"id":1,"name":"Alice"}
HTTPのように返します。
この「Accept と Content-Type の組み合わせ」で、
クライアントとサーバーが「どの形式で話すか」をすり合わせているイメージです。
初心者向けまとめ:Content-Type を自分の言葉で説明するなら
あなたの言葉で整理すると、こうなります。
Content-Type は、
「HTTP のボディに入っている中身が、どんな種類のデータかを示すヘッダー」で、application/json や text/html などのメディアタイプで表現する。
クライアントが JSON を送るときは Content-Type: application/json を付け、
サーバーが JSON を返すときも同じく application/json を付ける。
中身と Content-Type がズレると、パースエラーや表示崩れ、セキュリティ問題の原因になる。
Java(Spring)では、@RequestBody や @RestController を使うことで、
JSON 前提の Content-Type のやり取りが自然にできるようになっているが、
「何を送っていて、何を返しているのか」を意識して設計できるようになると、一気に一人前に近づく。
もしよければ、
「自分が作りたい API のリクエストとレスポンスの Content-Type は何か?」
を一つずつ言語化してみて。
それを説明できるようになったとき、Content-Type はもう“おまけの文字列”ではなく、“あなたがコントロールするプロトコルの一部”になっているはず。
