はじめに
「学生ひよこ界隈が送るGo/Javaで実現する「はじめてのバックエンド」Advent Calendar 2025」10日目の記事は、GoのAPI開発におけるCORS対応についてです。
「APIはPostmanで叩けるのに、フロントエンドからfetchすると繋がらない…」
とフロントエンドも触って間もない頃は、この現象に悩まされました。
ブラウザのコンソールに赤い文字で「CORS」と書かれていたときは、訳もわからないエラーが出てきた...と悩まされた方もいらっしゃるのではないでしょうか。
この記事では、CORS(オリジン間リソース共有)とその設定方法について解説します。
- ローカルでフロントエンドとバックエンドを開発していて、CORSエラーに遭遇した方
- 「CORSエラー」という言葉は知っているが、なぜ起きるのか仕組みを理解したい方
- GoのAPIサーバーで、どうやってCORSを許可すればいいか知りたい方
この記事で、GoのAPIサーバー側で適切な設定を行い、ローカル開発環境でフロントエンドとバックエンドをスムーズに接続できるようになれるように、頑張りましょう!
CORSとは?
フロントエンドとバックエンドをそれぞれ別のサーバー(あるいは別のポート)で開発していると、以下のようなエラーを見たことがある方も多いのではないでしょうか?
Access to fetch at 'http://localhost:8080/api/users' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
これは、CORS (Cross-Origin Resource Sharing)、日本語では「オリジン間リソース共有」と呼ばれる仕組みによって引き起こされる、ブラウザのエラーメッセージです。
これは、サーバー側のエラーではなく、ブラウザがセキュリティのためにリクエストをブロックしています。
オリジン(Origin)とは?
そもそもオリジンは、URLの「スキーム(プロトコル)」「ホスト(ドメイン)」「ポート番号」の3つの組み合わせで定義されます。
どれか一つでも違えば、ブラウザはそれを異なるOriginとして扱います。
例えば、httpとhttps、example.comとapp.example.com、example.com:80とexample.com:8080みたいな違いです。
CORSエラーは、あるオリジン(例: http://localhost:3000のフロントエンド)から、それとは異なるオリジン(例: http://localhost:8080のバックエンドAPI)のリソースにアクセスしようとした際に、アクセス先のサーバーがそれを許可していないと発生します。
CORSが発生する仕組み(同一オリジンポリシー)
CORSエラーの背景には、ブラウザが標準で備えている同一オリジンポリシー (Same-Origin Policy) という非常に重要なセキュリティ原則があります。
同一オリジンポリシーとは、あるオリジンから読み込まれた文書やスクリプトは、そのオリジンとは異なるオリジンのリソースとやり取りすることを原則として禁止する、というものです。
もしこのポリシーがなければ、悪意のあるWebサイト(例: http://evil.com)にアクセスしただけで、そのサイトのスクリプトが、あなたがログインしている別のサイト(例: http://my-bank.com)のAPIを勝手に叩き、情報を盗み出すことが可能になってしまいます。
同一オリジンポリシーは、このような危険なリクエストを防ぐための、ブラウザの基本的な防御機能なのです。
しかし、現代のWebアプリケーションでは、APIサーバーとフロントエンドのサーバーを分離するなど、異なるオリジン間でリソースを共有する正当な理由が数多くあります。そこで、この厳格なポリシーを安全に緩和するための仕組みがCORSです。
CORSは、サーバー側が「このオリジンからのリクエストなら許可しますよ」という意思表示をHTTPヘッダーを通じて行うことで、ブラウザにリソースへのアクセスを許可させる仕組みです。
【実践】Go APIでCORSを許可するための設定
Goのnet/httpを使って手動でCORS関連のヘッダーを設定することも可能ですが、考慮すべき点(OPTIONSメソッドへの対応など)が多く複雑になりがちです。
そこで、一般的にはCORS設定を簡単に行うためのミドルウェアライブラリを利用します。
ここでは、github.com/rs/corsを使った設定方法を紹介します。
go mod initをした状態で、以下のコマンドからライブラリをインストールしましょう。
go get github.com/rs/cors
main.goには以下のコードを記載します。
package main
import (
"fmt"
"log"
"net/http"
"github.com/rs/cors"
)
func helloHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Hello from Go API!")
}
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/api/hello", helloHandler)
// POINT: CORSポリシーを定義
c := cors.New(cors.Options{
// 許可するオリジンを指定
AllowedOrigins: []string{"http://localhost:3000", "http://localhost:5500"},
// 許可するHTTPメソッドを指定
AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
// 許可するリクエストヘッダーを指定
AllowedHeaders: []string{"Content-Type", "Authorization"},
// trueにすると、Cookieなどの認証情報を含むリクエストを許可
AllowCredentials: true,
})
// ルーターをCORSミドルウェアでラップ
handler := c.Handler(mux)
log.Println("サーバーを起動します: http://localhost:8080")
log.Fatal(http.ListenAndServe(":8080", handler))
}
上記のコードでは、まずmux := http.NewServeMux()で新しいルーター(ServeMux)を作成しています。ServeMuxは、受信したHTTPリクエストのURLパスに基づいて、適切なハンドラー関数にリクエストを振り分ける役割を担います。これにより、複数のAPIエンドポイントを管理できるようになります。
次に、mux.HandleFunc("/api/hello", helloHandler)では、特定のURLパス/api/helloに対して、helloHandlerという関数をハンドラーとして登録しています。つまり、http://localhost:8080/api/helloへのリクエストが来た場合、helloHandlerが実行され、「Hello from Go API!」というレスポンスが返されるようになります。
ここまでは通常のAPIの処理です。
CORSの許可ポリシーを定義しているのは、cors.New()の部分です。
cors.Optionsで、どのオリジンからのリクエストを、どのメソッドやヘッダーで許可するかを細かく設定できます。
このようにミドルウェアを使うことで、アプリケーション本体のロジックを変更することなく、CORS対応を簡単に追加できます。
そして、handler := c.Handler(mux)で、先ほど定義したCORSの設定cを使って、作成したルーターmuxをラップすることで、muxが処理する全てのリクエストに対して、CORSポリシーが適用されるようになります。
必要なヘッダー情報(Access-Control-Allow-Originなど)
rs/corsライブラリが内部で行っているのは、CORSの仕様で定められたHTTPレスポンスヘッダーを自動で付与することです。
先ほどのコードにも出てきた代表的なヘッダーをいくつか見てみましょう。
-
Access-Control-Allow-Origin
ここが最も重要で、リソースへのアクセスを許可するオリジンを指定します。
例:Access-Control-Allow-Origin: http://localhost:3000 -
Access-Control-Allow-Methods
リソースへのアクセスを許可するHTTPメソッドをカンマ区切りで指定します。
例:Access-Control-Allow-Methods: GET, POST, OPTIONS -
Access-Control-Allow-Headers
リクエストで許可されるHTTPヘッダーをカンマ区切りで指定します。
例:Access-Control-Allow-Headers: Content-Type, Authorization
これらのヘッダーがサーバーからのレスポンスに含まれていると、ブラウザは「この通信はサーバーが許可している安全なものだ」と判断し、フロントエンドのJavaScriptがレスポンスデータにアクセスすることを許可します。
フロントエンドでの簡単な動作確認
GoのAPIサーバーでCORS設定が完了したら、Goでのコードを実行してサーバーを起動しつつ、フロントエンド側から実際にリクエストを送って確認してみましょう。
ここでは、VS Codeの「Live Server」拡張機能を使って、素のJavaScriptのfetch APIでリクエストを送ってみます。
以下のindex.htmlを作成し、「Live Server」でページを開いてボタンを押してみてください。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>CORS Test</title>
</head>
<body>
<h1>CORS Test</h1>
<button id="fetchButton">Fetch API</button>
<p>Response: <span id="response"></span></p>
<script>
document.getElementById('fetchButton').addEventListener('click', () => {
const responseSpan = document.getElementById('response');
responseSpan.textContent = 'Loading...';
fetch('http://localhost:8080/api/hello')
.then(response => {
if (!response.ok) {
throw new Error('レスポンスがうまく返りませんでした');
}
return response.text();
})
.then(data => {
responseSpan.textContent = data;
})
.catch(error => {
console.error('Fetch error:', error);
responseSpan.textContent = 'Error: ' + error.message;
// CORSエラーの場合、コンソールに詳細が表示されます
});
});
</script>
</body>
</html>
ちなみにLive Serverは5500番のポートなので、もしうまくいかなければGoのオリジン設定を確認してみてください。
-
CORS設定が正しければ:
Response: Hello from Go API!と表示されます。 - CORS設定が間違っていれば: ブラウザのコンソールにCORSエラーが表示されます。
おわりに
この記事では、Go APIとフロントエンド間のCORSエラーを解決する方法について解説しました。
今回はCORSの基本的な設定について学びましたが、セキュリティとして堅牢な細かいCORS設定方法については触れていないので、今後の学習に活かしたいと思いました。
最後までお読みいただき、ありがとうございました!
この「学生ひよこ界隈が送るGo/Javaで実現する「はじめてのバックエンド」Advent Calendar 2025」では、GoやJavaを使い、APIの作り方、データベースとの接続、テストやDockerといった気になったバックエンド技術の基本を振り返った学びを共有しています。
ぜひ他の記事もチェックして、筆者がこのひとりアドカレを完遂することができるか、確認してみてください(^^)
学生ひよこ界隈が送るGo/Javaで実現する「はじめてのバックエンド」Advent Calendar 2025
それでは、明日の「学生ひよこ界隈が送るGo/Javaで実現する「はじめてのバックエンド」Advent Calendar 2025」の記事もお楽しみに!
参考文献