リクエスト先サーバー

パス役割
/HTMLを配信するエンドポイント
/api-full-corsCORSとCORS Preflight Request に対応したエンドポイント
/api-simple-corsCORSの「単純なリクエスト」のみ対応し、CORS Preflight Request に非対応のエンドポイント
/api-no-corsCORS非対応のエンドポイント

検証に使用したexpressサーバーのソースコード

クリックして展開
import express from "express";
import fs from "fs";
import https from "https";
import path from "path";

const app = express();

/**
 * パス: "/"
 * HTMLを配信するエンドポイント
 */
app.get("/", (req, res) => {
  res.send("<html><body><h1>hello world</h1></body></html>");
});

/**
 * パス: "/api-cors"
 * CORSとCORS Preflight Request に対応したエンドポイント
 */
app.post("/api-full-cors", (req, res, next) => {
  res.header("Access-Control-Allow-Origin", req.headers.origin);
  res.json({ result: "ok" });
  res.send();
});
app.options("/api-full-cors", (req, res) => {
  res.header("Access-Control-Allow-Origin", req.headers.origin);
  res.header("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
  res.header("Access-Control-Allow-Headers", "Content-Type, Authorization");
  res.send();
});

/**
 * パス: "/api-simple-cors"
 * CORSの「単純なリクエスト」のみ対応し、CORS Preflight Request に非対応のエンドポイント
 */
app.post("/api-simple-cors", (req, res, next) => {
  res.header("Access-Control-Allow-Origin", req.headers.origin);
  res.json({ result: "ok" });
  res.send();
});

/**
 * パス: "/api-no-cors"
 * CORS非対応のエンドポイント
 */
app.post("/api-no-cors", (req, res, next) => {
  res.json({ result: "ok" });
  res.send();
});

// HTTPSサーバーの起動
const options = {
  key: fs.readFileSync(path.join(__dirname, "..", "key.pem")),
  cert: fs.readFileSync(path.join(__dirname, "..", "cert.pem")),
};
https.createServer(options, app).listen(3000, () => {
  console.log("HTTPS Server running on port 3000");
});

// HTTPサーバーの起動
app.listen(3001, () => {
  console.log("HTTP Server running on port 3001");
});

// HTTPサーバーの起動(2)
app.listen(3002, () => {
  console.log("HTTP Server running on port 3002");
});

(補足) expressでは、実装していないOPTIONSリクエストに対してもレスポンスを自動的に返却してしまう。ただし今回の場合、自動的に実装されるレスポンスに関してはAccess-Control-Allow-ヘッダーが含まれないのでこのまま進める。

$ curl 'http://localhost:3001/api-simple-cors' -X 'OPTIONS' --head
HTTP/1.1 200 OK
X-Powered-By: Express
Allow: POST
Content-Type: text/html; charset=utf-8
Content-Length: 4
ETag: W/"4-Yf+Bwwqjx254r+pisuO9HfpJ6FQ"
Date: Sun, 21 Jul 2024 08:21:08 GMT
Connection: keep-alive
Keep-Alive: timeout=5

ケース 1

リクエスト条件

スキーマSSL証明書オリジンリクエストの内容
HTTPn/a (HTTPのため未使用)同じ単純

結果

http://localhost:3001/ にアクセス後、Consoleで実行して確認する

api-full-corsapi-simple-corsapi-no-cors
OKOKOK
var res = await fetch("http://localhost:3001/api-full-cors", {headers: {"Content-Type": "text/plain"}, body: "abc", method: "POST"})
console.log(res.ok) // true

var res = await fetch("http://localhost:3001/api-simple-cors", {headers: {"Content-Type": "text/plain"}, body: "abc", method: "POST"})
console.log(res.ok) // true

var res = await fetch("http://localhost:3001/api-no-cors", {headers: {"Content-Type": "text/plain"}, body: "abc", method: "POST"})
console.log(res.ok) // true

ケース 2

リクエスト条件

スキーマSSL証明書オリジンリクエストの内容
HTTPn/a (HTTPのため未使用)同じ単純でない

結果

http://localhost:3001/ にアクセス後、Consoleで実行して確認する

api-full-corsapi-simple-corsapi-no-cors
OKOKOK
var res = await fetch("http://localhost:3001/api-full-cors", {headers: {"Content-Type": "application/json"}, body: JSON.stringify({data: "abc"}), method: "POST"})
console.log(res.ok) // true

var res = await fetch("http://localhost:3001/api-simple-cors", {headers: {"Content-Type": "application/json"}, body: JSON.stringify({data: "abc"}), method: "POST"})
console.log(res.ok) // true

var res = await fetch("http://localhost:3001/api-no-cors", {headers: {"Content-Type": "application/json"}, body: JSON.stringify({data: "abc"}), method: "POST"})
console.log(res.ok) // true

ケース 3

リクエスト条件

スキーマSSL証明書オリジンリクエストの内容
HTTPn/a (HTTPのため未使用)異なる単純

結果

http://localhost:3001/ にアクセス後、Consoleで実行して確認する

api-full-corsapi-simple-corsapi-no-cors
OKOKNG
var res = await fetch("http://localhost:3002/api-full-cors", {headers: {"Content-Type": "text/plain"}, body: "abc", method: "POST"})
console.log(res.ok) // true

var res = await fetch("http://localhost:3002/api-simple-cors", {headers: {"Content-Type": "text/plain"}, body: "abc", method: "POST"})
console.log(res.ok) // true

var res = await fetch("http://localhost:3002/api-no-cors", {headers: {"Content-Type": "text/plain"}, body: "abc", method: "POST"})
console.log(res.ok) // CORSエラー

ケース 4

リクエスト条件

スキーマSSL証明書オリジンリクエストの内容
HTTPn/a (HTTPのため未使用)異なる単純でない

結果

http://localhost:3001/ にアクセス後、Consoleで実行して確認する

api-full-corsapi-simple-corsapi-no-cors
OKNGNG
var res = await fetch("http://localhost:3002/api-full-cors", {headers: {"Content-Type": "application/json"}, body: JSON.stringify({data: "abc"}), method: "POST"})
console.log(res.ok) // true

var res = await fetch("http://localhost:3002/api-simple-cors", {headers: {"Content-Type": "application/json"}, body: JSON.stringify({data: "abc"}), method: "POST"})
console.log(res.ok) // CORSエラー

var res = await fetch("http://localhost:3002/api-no-cors", {headers: {"Content-Type": "application/json"}, body: JSON.stringify({data: "abc"}), method: "POST"})
console.log(res.ok) // CORSエラー

ケース 5

リクエスト条件

スキーマSSL証明書オリジンリクエストの内容
HTTPS異常 (オレオレ証明書な場合)同じ単純

結果

https://localhost:3000 にアクセスし、証明書エラーをAcceptしてからConsoleで実行して確認する。

api-full-corsapi-simple-corsapi-no-cors
OKOKOK
var res = await fetch("https://localhost:3000/api-full-cors", {headers: {"Content-Type": "text/plain"}, body: "abc", method: "POST"})
console.log(res.ok) // true

var res = await fetch("https://localhost:3000/api-simple-cors", {headers: {"Content-Type": "text/plain"}, body: "abc", method: "POST"})
console.log(res.ok) // true

var res = await fetch("https://localhost:3000/api-no-cors", {headers: {"Content-Type": "text/plain"}, body: "abc", method: "POST"})
console.log(res.ok) // true

ケース 6

リクエスト条件

スキーマSSL証明書オリジンリクエストの内容
HTTPS異常 (オレオレ証明書な場合)同じ単純でない

結果

https://localhost:3000 にアクセスし、証明書エラーをAcceptしてからConsoleで実行して確認する

api-full-corsapi-simple-corsapi-no-cors
OKOKOK
var res = await fetch("https://localhost:3000/api-full-cors", {headers: {"Content-Type": "application/json"}, body: JSON.stringify({data: "abc"}), method: "POST"})
console.log(res.ok) // true

var res = await fetch("https://localhost:3000/api-simple-cors", {headers: {"Content-Type": "application/json"}, body: JSON.stringify({data: "abc"}), method: "POST"})
console.log(res.ok) // true

var res = await fetch("https://localhost:3000/api-no-cors", {headers: {"Content-Type": "application/json"}, body: JSON.stringify({data: "abc"}), method: "POST"})
console.log(res.ok) // true

ケース 7

リクエスト条件

スキーマSSL証明書オリジンリクエストの内容
HTTPS異常 (オレオレ証明書な場合)異なる単純

結果

一度 https://localhost:3000 にアクセスし、証明書エラーをAcceptする。 その後、 https://example.com にアクセスし、そのConsoleで実行して確認する

api-full-corsapi-simple-corsapi-no-cors
OKOKNG
var res = await fetch("https://localhost:3000/api-full-cors", {headers: {"Content-Type": "text/plain"}, body: "abc", method: "POST"})
console.log(res.ok) // true

var res = await fetch("https://localhost:3000/api-simple-cors", {headers: {"Content-Type": "text/plain"}, body: "abc", method: "POST"})
console.log(res.ok) // true

var res = await fetch("https://localhost:3000/api-no-cors", {headers: {"Content-Type": "text/plain"}, body: "abc", method: "POST"})
console.log(res.ok) // CORSエラー

ケース 8

リクエスト条件

スキーマSSL証明書オリジンリクエストの内容
HTTPS異常 (オレオレ証明書な場合)異なる単純でない

結果

一度 https://localhost:3000 にアクセスし、証明書エラーをAcceptする。 その後、 https://example.com にアクセスし、そのConsoleで実行して確認する

api-full-corsapi-simple-corsapi-no-cors
OKNGNG
var res = await fetch("https://localhost:3000/api-full-cors", {headers: {"Content-Type": "application/json"}, body: JSON.stringify({data: "abc"}), method: "POST"})
console.log(res.ok) // true

var res = await fetch("https://localhost:3000/api-simple-cors", {headers: {"Content-Type": "application/json"}, body: JSON.stringify({data: "abc"}), method: "POST"})
console.log(res.ok) // CORSエラー

var res = await fetch("https://localhost:3000/api-no-cors", {headers: {"Content-Type": "application/json"}, body: JSON.stringify({data: "abc"}), method: "POST"})
console.log(res.ok) // CORSエラー

ケース 9

リクエスト条件

スキーマSSL証明書オリジンリクエストの内容
HTTPS正常同じ単純

結果

ngrok等を使って正しく署名された URL (例: https://sample.test) にアクセスしてからConsoleで実行して確認する

api-full-corsapi-simple-corsapi-no-cors
OKOKOK
var res = await fetch("https://sample.test/api-full-cors", {headers: {"Content-Type": "text/plain"}, body: "abc", method: "POST"})
console.log(res.ok) // true

var res = await fetch("https://sample.test/api-simple-cors", {headers: {"Content-Type": "text/plain"}, body: "abc", method: "POST"})
console.log(res.ok) // true

var res = await fetch("https://sample.test/api-no-cors", {headers: {"Content-Type": "text/plain"}, body: "abc", method: "POST"})
console.log(res.ok) // true

ケース 10

リクエスト条件

スキーマSSL証明書オリジンリクエストの内容
HTTPS正常同じ単純でない

結果

ngrok等を使って正しく署名された https://[URL] にアクセスしてからConsoleで実行して確認する

api-full-corsapi-simple-corsapi-no-cors
OKOKOK
var res = await fetch("https://sample.test/api-full-cors", {headers: {"Content-Type": "application/json"}, body: JSON.stringify({data: "abc"}), method: "POST"})
console.log(res.ok) // true

var res = await fetch("https://sample.test/api-simple-cors", {headers: {"Content-Type": "application/json"}, body: JSON.stringify({data: "abc"}), method: "POST"})
console.log(res.ok) // true

var res = await fetch("https://sample.test/api-no-cors", {headers: {"Content-Type": "application/json"}, body: JSON.stringify({data: "abc"}), method: "POST"})
console.log(res.ok) // true

ケース 11

リクエスト条件

スキーマSSL証明書オリジンリクエストの内容
HTTPS正常異なる単純

結果

https://example.com にアクセスしてからConsoleで実行して確認する

api-full-corsapi-simple-corsapi-no-cors
OKOKNG
var res = await fetch("https://sample.test/api-full-cors", {headers: {"Content-Type": "text/plain"}, body: "abc", method: "POST"})
console.log(res.ok) // true

var res = await fetch("https://sample.test/api-simple-cors", {headers: {"Content-Type": "text/plain"}, body: "abc", method: "POST"})
console.log(res.ok) // true

var res = await fetch("https://sample.test/api-no-cors", {headers: {"Content-Type": "text/plain"}, body: "abc", method: "POST"})
console.log(res.ok) // CORSエラー

ケース 12

リクエスト条件

スキーマSSL証明書オリジンリクエストの内容
HTTPS正常異なる単純でない

結果

https://example.com にアクセスしてからConsoleで実行して確認する

api-full-corsapi-simple-corsapi-no-cors
OKNGNG
var res = await fetch("https://sample.test/api-full-cors", {headers: {"Content-Type": "application/json"}, body: JSON.stringify({data: "abc"}), method: "POST"})
console.log(res.ok) // true

var res = await fetch("https://sample.test/api-simple-cors", {headers: {"Content-Type": "application/json"}, body: JSON.stringify({data: "abc"}), method: "POST"})
console.log(res.ok) // CORSエラー

var res = await fetch("https://sample.test/api-no-cors", {headers: {"Content-Type": "application/json"}, body: JSON.stringify({data: "abc"}), method: "POST"})
console.log(res.ok) // CORSエラー