Skip to content

b13/rate-limiting

Repository files navigation

TYPO3 Extension: Rate Limiting

This TYPO3 extension provides a frontend middleware for rate limiting requests based on configurable rules.

What does it do?

In case your server or hoster cannot deal with Rate Limiting, but your system is typically under heavy load, this extension is for you. It is based on the symfony/rate-limiting component.

  • IP-based rate limiting: Limit requests per IP address
  • URL pattern filtering: Apply rate limiting only to specific URLs (e.g., search, forms)
  • HTTP method filtering: Rate limit only specific HTTP methods (GET, POST, etc.)
  • User-Agent filtering: Rate limit specific bots, crawlers, or API clients
  • IP masking: Group requests by IP blocks (useful for proxies or shared IPs)
  • Caching Framework storage: Uses TYPO3's Caching Framework for persistence
  • Site-specific configuration: Configure different limits per TYPO3 site
  • DB logging: Every blocked request is written to tx_rate_limiting_log
  • Enrichment event: A PSR-14 event lets any extension add extra fields to the log entry
  • Configurable 429 page: Uses the site's error handler for status 429 when configured

Installation

  1. Install the extension via TER or composer req b13/rate-limiting
  2. Activate the extension in the Extension Manager whe installed via TER.
  3. Configure via Site Settings (see Configuration section)

Configuration

Configuration is done via Site Sets.

Available Settings

rateLimiting.enabled

  • Type: Boolean
  • Default: false
  • Description: Enable or disable rate limiting for this site

rateLimiting.limit

  • Type: Integer
  • Default: 100
  • Description: Maximum number of requests allowed within the interval

rateLimiting.interval

  • Type: String
  • Default: 1 minute
  • Description: Time interval for the rate limit
  • Examples: 30 seconds, 1 minute, 5 minutes, 1 hour

rateLimiting.ipMaskLength

  • Type: Integer
  • Default: 0 (full IP)
  • Description: Number of IP blocks to use for grouping
  • Examples:
    • 0: Full IP (192.168.1.100)
    • 2: First 2 blocks (192.168.0.0)
    • 3: First 3 blocks (192.168.1.0)

rateLimiting.urlPatterns

  • Type: String list
  • Default: [] (all requests)
  • Description: Only rate limit requests matching these URL patterns
  • Examples:
    • tx_solr: Rate limit Solr search requests
    • tx_form: Rate limit form submissions
    • /api/: Rate limit API endpoints

rateLimiting.methods

  • Type: String list
  • Default: [] (all methods)
  • Description: Only rate limit specific HTTP methods
  • Examples: POST, PUT, DELETE

rateLimiting.userAgentPatterns

  • Type: String list
  • Default: [] (all user agents)
  • Description: Only rate limit requests with matching User-Agent headers
  • Supports: Simple string matching and wildcard patterns (* and ?)
  • Examples:
    • Googlebot: Rate limit Google's crawler
    • bot: Rate limit any user agent containing "bot"
    • *crawler*: Wildcard matching for any crawler
    • curl*: Rate limit curl and similar tools
    • Python-*: Rate limit Python HTTP clients

Example Configurations

Example 1: Rate limit search requests

settings:
  rateLimiting:
    enabled: true
    limit: 50
    interval: '1 minute'
    urlPatterns:
      - 'tx_solr'

Example 2: Rate limit POST requests with IP grouping

settings:
  rateLimiting:
    enabled: true
    limit: 20
    interval: '1 minute'
    ipMaskLength: 2
    methods:
      - POST

Example 3: Rate limit form submissions

settings:
  rateLimiting:
    enabled: true
    limit: 10
    interval: '5 minutes'
    urlPatterns:
      - 'tx_form'
    methods:
      - POST

Example 4: Rate limit bots and crawlers

settings:
  rateLimiting:
    enabled: true
    limit: 30
    interval: '1 minute'
    userAgentPatterns:
      - 'bot'
      - 'crawler'
      - 'spider'

Example 5: Rate limit specific API clients

settings:
  rateLimiting:
    enabled: true
    limit: 100
    interval: '1 hour'
    urlPatterns:
      - '/api/'
    userAgentPatterns:
      - 'curl*'
      - 'Python-*'
      - 'PostmanRuntime*'

Logging

DB logging is disabled by default. Enable it per site via the rateLimiting.logging setting:

settings:
  rateLimiting:
    enabled: true
    logging: true

Every request that hits the rate limit is then written to the tx_rate_limiting_log database table with the following fields:

Field Description
ip Remote IP address
timestamp Unix timestamp of the blocked request
time Human-readable datetime (Y-m-d H:i:s)
domain Host from the request URI
query JSON-encoded query parameters
user_agent User-Agent header

Log entries are automatically cleaned up after 90 days when TYPO3's Table garbage collection scheduler task is active.

Enriching log entries with custom fields

Before writing to the database the middleware dispatches a B13\RateLimiting\Event\RateLimitExceededEvent. Any event listener can call addLogField(string $key, mixed $value) to inject additional columns into the log record. The column must already exist in the table (add it via your extension's ext_tables.sql).

Example: store the visitor's country

  1. Extend the table in your extension's ext_tables.sql:
CREATE TABLE tx_rate_limiting_log (
    country varchar(12) default '' not null
);
  1. Register an event listener (TYPO3 13+):
<?php

declare(strict_types=1);

namespace Vendor\MyExtension\EventListener;

use B13\RateLimiting\Event\RateLimitExceededEvent;
use TYPO3\CMS\Core\Attribute\AsEventListener;
use TYPO3\CMS\Core\Http\NormalizedParams;

#[AsEventListener]
class EnrichRateLimitLogEntry
{
    public function __invoke(RateLimitExceededEvent $event): void
    {
        $normalizedParams = $event->getRequest()->getAttribute('normalizedParams')
            ?? NormalizedParams::createFromRequest($event->getRequest());

        // Resolve the country by whichever means fits your project, then:
        $event->addLogField('country', $this->resolveCountry($normalizedParams->getRemoteAddress()));
    }

    private function resolveCountry(string $ip): string
    {
        // your geo-IP lookup here
        return '';
    }
}

The listener is auto-registered through autoconfigure: true in Services.yaml and the #[AsEventListener] attribute — no further configuration required.

Customising the 429 response

By default the middleware returns a plain TYPO3 error page for HTML requests and a JSON response for requests that send Accept: application/json.

To serve a fully styled error page, configure a Page error handler for HTTP status 429 in the site module. The middleware will automatically delegate to that handler when it is present.

License

The extension is licensed under GPL v2+, same as the TYPO3 Core. For details see the LICENSE file in this repository.

Credits

This extension was created by Benni Mack in 2025 for b13 GmbH, Stuttgart.

Find more TYPO3 extensions we have developed that help us deliver value in client projects. As part of our work, we focus on testing and best practices to ensure long-term performance, reliability, and results in all our code.

About

Rate Limiting for some TYPO3 pages

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Languages