はじめに
今回は2026年6月にRFC 10008として標準化されたHTTPメソッド QUERY を実際にPHPで使ってみようと思います。こちらがQUERYメソッドの標準仕様になります。
RFC 10008: The HTTP QUERY MethodThis specification defines the QUERY method for HTTP. A QUERY requests that the request target process the enclosed cont…
検証に用いた環境がこちらです。Dockerで環境を構築しました。
PHP: php8.3-fpm
Nginx: nginx 1.31.2
ディレクトリ構成
php-querymethod
┗ docker-config
┗ nginx
┗ Dockerifleとdefault.conf
┗ php
┗ Dockerfileとphp.ini
┗ src
┗ public/index.php(フロントコントローラ)
┗ .env
┗ .env.example
┗ .gitignore
┗ docker-compose.yml
検証用で使ったリポジトリはこちらになります。
GitHub – yu-corder/php-querymethod: A PHP project for experimenting with the HTTP QUERY method defined in RFC 10008.A PHP project for experimenting with the HTTP QUERY method defined in RFC 10008. – yu-corder/php-querymethod
QUERYメソッドとは
QUERYメソッドが策定された背景としては、GETのような読み取り操作になりますが、URLでは表現しづらい複雑な検索条件をBodyで送るために策定されました。QUERYメソッドはGETと同じくsafe/idempotentなメソッドですが、URLではなくrequest bodyに検索条件を含め、その結果となる表現を取得するために設計されています。
GETではURL Query Stringで検索条件を表現しますが、複雑な条件になるとURL長や可読性の問題があります。QUERYではrequest bodyに検索条件を含めることで、GETでは表現しづらかった複雑な検索処理を標準的なHTTPメソッドとして扱えるようになります。
例えばですが、GETメソッドでリソースを取得する際は、URLに条件を組み込んで取得してました。
GET /users?name=Alice&age=30&country=Japan&...
もっと複雑な条件で取得する場合は、URL長さ制限にも引っかかります。QUERYが登場する前はPOSTメソッドで複雑な検索などもしてましたが、POSTはsafe/idempotentではなく、HTTPキャッシュとの相性もGETほど一般的ではありません。
GETやPOSTメソッドとの違いは下記になります。
| 項目 | GET | POST | QUERY |
|---|---|---|---|
| 主な目的 | リソースの取得 | データ送信・状態変更 | 検索クエリの送信 |
| 冪等性 | あり | 非冪等 | あり |
| 安全性(Safe Method) | あり | なし | あり |
| Request Body | 推奨されない | 利用する | 利用する |
| URL Query String | よく使う | あまり使わない | 基本不要 |
| 主用途 | /users/1を取得 | ユーザー登録、フォーム送信 | 複雑な検索条件送信 |
| キャッシュ | 可能 | 基本不可 | 可能 |
| HTMLフォーム対応 | <form method="GET"> | <form method="POST"> | 未対応 |
| REST的意味 | リソース取得 | リソース作成・処理実行 | 検索処理 |
PHPとnginxだけで検証
nginx の設定がこちらになります。他のプロジェクトで使っていた、default.confをそのまま使っています。制限等はしていないので、QUERYメソッドも受け付けるはずです。
server {
listen 80;
server_name localhost;
root /var/www/public;
index index.php index.html;
allow all;
client_max_body_size 10000M;
access_log /var/log/nginx/ssl-access.log;
error_log /var/log/nginx/ssl-error.log;
location / {
rewrite ^ /index.php last;
}
location ~ \.php$ {
try_files $uri =404;
#502
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass web:9000;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods "POST, GET, QUERY, OPTIONS";
add_header Access-Control-Allow-Headers "Origin, Authorization, Accept";
add_header Access-Control-Allow-Credentials true;
}
}
index.php(フロントコントローラ)
受け付けたリクエストのmethod をそのまま表示します。
<?php
header('Content-Type: application/json');
echo json_encode([
'method' => $_SERVER['REQUEST_METHOD'],
'uri' => $_SERVER['REQUEST_URI'],
'content_type' => $_SERVER['CONTENT_TYPE'] ?? null,
'body' => file_get_contents('php://input'),
'print' => 'Hello World!',
], JSON_PRETTY_PRINT);
リクエストを投げてみます。ボディを付与して、リクエストを投げてます。
test@tmac php-querymethod % curl -i \
-X QUERY \
http://localhost:8080/ \
-H "Content-Type: application/json" \
-d '{"name":"Alice","age":30}'
HTTP/1.1 200 OK
Server: nginx/1.31.2
Date: Fri, 10 Jul 2026 14:39:45 GMT
Content-Type: application/json
Transfer-Encoding: chunked
Connection: keep-alive
X-Powered-By: PHP/8.3.32
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: POST, GET, QUERY, OPTIONS
Access-Control-Allow-Headers: Origin, Authorization, Accept
Access-Control-Allow-Credentials: true
{
"method": "QUERY",
"uri": "\/",
"content_type": "application\/json",
"body": "{\"name\":\"Alice\",\"age\":30}",
"print": "Hello World!"
}%
test@tmac php-querymethod %
正常にリクエストを受け取れてますね。
Laravelで検証
web.phpでany で全てを許可するようにします。
Route::any('/', function (Illuminate\Http\Request $request) {
dd(
$request->method(),
$_SERVER['REQUEST_METHOD'],
$request->getContent()
);
});
リクエストを投げてみます。
HTTP/1.1 405 Method Not Allowed
Server: nginx/1.31.2
Content-Type: text/html; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
X-Powered-By: PHP/8.3.32
allow: GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS
Cache-Control: no-cache, private
date: Fri, 10 Jul 2026 15:31:39 GMT
Laravelのany は厳密には下記のメソッドしかサポートしていないということですかね。(LaravelのRoute::any()は「全HTTPメソッド」という名前ですが、内部的にはLaravelが定義している標準的なHTTPメソッドのみを対象にしています。)
[
'GET',
'HEAD',
'POST',
'PUT',
'PATCH',
'DELETE',
'OPTIONS'
]
少し書き方を変えて、リクエストを投げてみます。
<?php
use Illuminate\Support\Facades\Route;
Route::match(['QUERY'], '/', function (Illuminate\Http\Request $request) {
return response()->json([
'method' => $request->method(),
'body' => $request->getContent(),
]);
});
src/bootstrap/app.phpの記述を修正します。(csrfトークンのエラーが出るため。)これはLaravelのin_array(‘QUERY’, [‘HEAD’,’GET’,’OPTIONS’]) でSafe MethodはCSRF検証対象外として扱われており、QUERYメソッドはこれに該当しないから、CSRFトークンのチェックによりエラーが出るみたいですね。※注意: 以下の検証は、Laravel内部の挙動を理解するための実験(PoC)です。実務(プロダクション環境)での利用はセキュリティリスクやメンテナンス性の観点から全く推奨しません。
<?php
use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware;
return Application::configure(basePath: dirname(__DIR__))
->withRouting(
web: __DIR__.'/../routes/web.php',
commands: __DIR__.'/../routes/console.php',
health: '/up',
)
->withMiddleware(function (Middleware $middleware): void {
$middleware->validateCsrfTokens(except: [
'/',
]);
})
->withExceptions(function (Exceptions $exceptions): void {
//
})->create();
test@tmac php-querymethod % curl -i \
-X QUERY \
http://localhost:8080/ \
-H "Content-Type: application/json" \
-d '{"name":"Alice","age":30}'
HTTP/1.1 200 OK
Server: nginx/1.31.2
Content-Type: application/json
Transfer-Encoding: chunked
Connection: keep-alive
X-Powered-By: PHP/8.3.32
Cache-Control: no-cache, private
Date: Fri, 10 Jul 2026 17:03:40 GMT
Set-Cookie: XSRF-TOKEN=eyJpdiI6IndCdlI2Ri8vQU9CRVF4Y1BURHVoUUE9PSIsInZhbHVlIjoibEhLM0ZDV3YxbDJYNTVwbnpuTVJFMzJPV3dpc1J4bXlnNXBqUTI4c0FrVmhMSGhNb1FVbVkvV2ZYY2tObG1YdmgvcEl5Sm5LY1Q3WDlKUTh6eTNuS1lDT3J0SHVhcGtnUEJpOFdzZUYrVGxLUVNCQjB4d1dxWUJrbVdqTCtvczMiLCJtYWMiOiJiNDY0ZDE0NThiZTFhZmMxNTMwYWMyY2NmMmM1YzcxZTUyYTA4NGY1MThmOTNlOWVmODUzOWFjZjY2YTFiODczIiwidGFnIjoiIn0%3D; expires=Fri, 10 Jul 2026 19:03:40 GMT; Max-Age=7200; path=/; samesite=lax
Set-Cookie: laravel-session=eyJpdiI6ImZDS1FyMmI0NnNrRm9OeE9EMGVQd2c9PSIsInZhbHVlIjoiSnBOdXpEZEFEb0JsWUlLWTZaUnkveE1DcDJva3JXcjFjNkdmakU2YjlMZE5xOHJVbmgwT1V6am93RTJPa0d1TVVSV21wcjdGd1ViWE9TUFhKOHdKMGFYR0plYVRYL3JmRmdra1BvTjVJQ0UwWTdUdzlLWEFZb3lvS2NnM3FnZDUiLCJtYWMiOiI3ZGQzMDAxOTM1MmZiMzAxZDZlNjQyMTlmNWM1MTYzNzk5NTk2ZWRiNTk4NWYwOWM2M2VhN2M3MGFiNThkNjFiIiwidGFnIjoiIn0%3D; expires=Fri, 10 Jul 2026 19:03:40 GMT; Max-Age=7200; path=/; httponly; samesite=lax
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: POST, GET, QUERY, OPTIONS
Access-Control-Allow-Headers: Origin, Authorization, Accept
Access-Control-Allow-Credentials: true
{"method":"QUERY","body":"{\"name\":\"Alice\",\"age\":30}"}%
結果としては、Laravel はまだQUERYをサポートしていないですね。6月に標準仕様として追加されたばかりなので、当然ですが、、、
最後に
今回の検証のまとめがこちらになります。Nginx ではQUERYメソッドを使うことができます。ミドルウェア層ではまだ、サポート外となります。現時点では、Laravel等の主要フレームワークが公式にQUERYメソッドをサポート(Safe Methodとして認識、ルーティングの標準対応など)するのを待つのが賢明です。
| 層 | 結果 | 確認内容 |
|---|---|---|
| curl | ✅ | 任意のHTTPメソッドを送信可能 |
| nginx | ✅ | QUERYを拒否しない |
| PHP-FPM | ✅ | $_SERVER['REQUEST_METHOD']で取得可能 |
| Laravel Router | ❌(非推奨) | Route::any() は未対応。Route::match() での強制通過は可能だが実務利用は非推奨 |
| Laravel Request | ✅ | $request->method()で取得可能 |
| Request Body | ✅ | $request->getContent()で取得可能 |
| CSRF Middleware | ❌(非推奨) | Safe Method(保護対象外)として認識されないため、手動除外が必要になり脆弱性の原因になる可能性があります。 |
(おまけ) 検証の合間に独自メソッドも試してみました。HTTP仕様上、method tokenは拡張可能なため、HTTPサーバ自体は未知のメソッドでも受信できます。(ただし、そのメソッドをアプリケーションやフレームワークがどのように扱うかは、それぞれの実装に依存します。)
test@tmac php-querymethod % curl -i \
-X AIUEO \
http://localhost:8080/ \
-H "Content-Type: application/json" \
-d '{"name":"Alice","age":30}'
HTTP/1.1 200 OK
Server: nginx/1.31.2
Date: Fri, 10 Jul 2026 14:45:09 GMT
Content-Type: application/json
Transfer-Encoding: chunked
Connection: keep-alive
X-Powered-By: PHP/8.3.32
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: Origin, Authorization, Accept
Access-Control-Allow-Credentials: true
{
"method": "AIUEO",
"uri": "\/",
"content_type": "application\/json",
"body": "{\"name\":\"Alice\",\"age\":30}",
"print": "Hello World!"
}%



コメント