RFC 10008で追加されたHTTP QUERYメソッドをPHP・Laravelで検証してみた

QUERYメソッドについての記事のアイキャッチ API

はじめに

今回は2026年6月にRFC 10008として標準化されたHTTPメソッド QUERY を実際にPHPで使ってみようと思います。こちらがQUERYメソッドの標準仕様になります。

RFC 10008: The HTTP QUERY Method
This 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メソッドとの違いは下記になります。

項目GETPOSTQUERY
主な目的リソースの取得データ送信・状態変更検索クエリの送信
冪等性あり非冪等あり
安全性(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メソッドを送信可能
nginxQUERYを拒否しない
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!"
}%                                               

コメント

タイトルとURLをコピーしました