---
title: "HTTP requests to external sources"
manual: "TYPO3 Explained"
version: "main"
permalink: "https://docs.typo3.org/permalink/t3coreapi:http@main"
source: "ExtensionArchitecture/HowTo/RestRequests/Index.rst"
rendered: "2026-09-20T15:52:37+00:00"
---

# HTTP requests to external sources {#http}

The PHP library "Guzzle" is available in TYPO3 as a feature-rich solution for
creating HTTP requests based on the PSR-7 interfaces.

Guzzle automatically detects the underlying adapters available on the system,
such as cURL or stream wrappers, and chooses the best solution for the system.

A TYPO3-specific PHP class named `\TYPO3\CMS\Core\Http\RequestFactory` is
present as a simplified wrapper for accessing Guzzle clients.

All options available under `$GLOBALS['TYPO3_CONF_VARS']['HTTP']` are
automatically applied to the Guzzle clients when using the `RequestFactory`
class. The options are a subset of the available options from Guzzle, but can be
extended.

> [!NOTE]
> **See also**
>
> -   [HTTP - tune requests](https://docs.typo3.org/permalink/t3coreapi:typo3confvars-http@main)
> -   [Guzzle request options](https://docs.guzzlephp.org/en/stable/request-options.html)
> -   [Full documentation for Guzzle](https://docs.guzzlephp.org/en/stable/)

Although Guzzle can handle Promises/A+ and asynchronous requests, it currently
serves as a drop-in replacement for the previous mixed options and
implementations within `GeneralUtility::getUrl()` and a PSR-7-based API for
HTTP requests.

The TYPO3-specific wrapper `GeneralUtility::getUrl()` uses Guzzle for
remote files, eliminating the need to directly configure settings based on
specific implementations such as stream wrappers or cURL.

## Basic usage {#http-basic}

The `RequestFactory` class can be used like this:

**EXT:examples/Classes/Http/MeowInformationRequester.php**

```php
<?php

declare(strict_types=1);

/*
 * This file is part of the TYPO3 CMS project. [...]
 */

namespace T3docs\Examples\Http;

use TYPO3\CMS\Core\Http\RequestFactory;

final readonly class MeowInformationRequester
{
  private const API_URL = 'https://catfact.ninja/fact';

  // We need the RequestFactory for creating and sending a request,
  // so we inject it into the class using constructor injection.
  public function __construct(
    private RequestFactory $requestFactory,
  ) {}

  /**
   * @throws \JsonException
   * @throws \RuntimeException
   */
  public function request(): string
  {
    // Additional headers for this specific request
    // See: https://docs.guzzlephp.org/en/stable/request-options.html
    $additionalOptions = [
      'headers' => ['Cache-Control' => 'no-cache'],
      'allow_redirects' => false,
    ];

    // Get a PSR-7-compliant response object
    $response = $this->requestFactory->request(
      self::API_URL,
      'GET',
      $additionalOptions,
    );

    if ($response->getStatusCode() !== 200) {
      throw new \RuntimeException(
        'Returned status code is ' . $response->getStatusCode(),
        4696129526,
      );
    }

    if ($response->getHeaderLine('Content-Type') !== 'application/json') {
      throw new \RuntimeException(
        'The request did not return JSON data',
        2722965511,
      );
    }
    // Get the content as a string on a successful request
    $content = $response->getBody()->getContents();
    $result = json_decode($content, true, flags: JSON_THROW_ON_ERROR);
    if (!is_array($result) || !isset($result['fact']) || !is_scalar($result['fact'])) {
      throw new \RuntimeException('The service returned an unexpected format.', 1666413230);
    }
    return (string)$result['fact'];
  }
}

```

A POST request can be achieved with:

**EXT:my_extension/Classes/SomeClass.php**

```php
<?php

declare(strict_types=1);

namespace MyVendor\MyExtension;

use Psr\Http\Message\ResponseInterface;
use TYPO3\CMS\Core\Http\RequestFactory;

final readonly class SomeClass
{
  public function __construct(
    private RequestFactory $requestFactory,
  ) {}

  public function post(string $url): ResponseInterface
  {
    $additionalOptions = [
      'body' => 'Your raw post data',
      // OR form data:
      'form_params' => [
        'first_name' => 'Jane',
        'last_name' => 'Doe',
      ],
    ];

    return $this->requestFactory->request($url, 'POST', $additionalOptions);
  }
}

```

Extension authors are advised to use the `RequestFactory` class instead of
using the Guzzle API directly in order to ensure a clear upgrade path when
updates to the underlying API need to be done.

## Custom middleware handlers {#http-custom-handlers}

Guzzle accepts a stack of custom middleware handlers which can be configured
in `$GLOBALS['TYPO3_CONF_VARS']['HTTP']['handler']` as an array. If a
custom configuration is specified, the default handler stack will be extended
and not overwritten.

**config/system/additional.php | typo3conf/system/additional.php**

```php
<?php

use MyVendor\MyExtension\Middleware\Guzzle\CustomMiddleware;
use MyVendor\MyExtension\Middleware\Guzzle\SecondCustomMiddleware;
use TYPO3\CMS\Core\Utility\GeneralUtility;

// Add custom middlewares to default Guzzle handler stack
$GLOBALS['TYPO3_CONF_VARS']['HTTP']['handler'][] =
  (GeneralUtility::makeInstance(CustomMiddleware::class))->handler();
$GLOBALS['TYPO3_CONF_VARS']['HTTP']['handler'][] =
  (GeneralUtility::makeInstance(SecondCustomMiddleware::class))->handler();

```

## HTTP utility methods {#http-http-utility-methods}

TYPO3 provides a small set of helper methods related to HTTP Requests in the class `HttpUtility`:

### `HttpUtility::buildUrl` {#http-http-utility-methods-httputility-buildurl}

Creates a URL string from an array containing the URL parts, such as those
output by `parse_url()`.

### `HttpUtility::buildQueryString` {#http-http-utility-methods-httputility-buildquerystring}

The `buildQueryString()` method is an enhancement to the [PHP function](https://www.php.net/manual/en/function.http-build-query.php)
`http_build_query()`. It implodes multidimensional parameter arrays and
properly encodes parameter names as well as values into a valid query string
with an optional prepend of `?` or `&`.

If the query is not empty, `?` or `&` are prepended in the correct sequence.
Empty parameters are skipped.
