Skip to main content

eea.api.redirector

Develop Master

The eea.api.redirector is a Plone add-on that extends Plone’s native redirect system with Redis-based URL redirects, enabling high-performance redirect lookups and external redirect management.

Main features

  1. Redis-backed redirects: Store and retrieve URL redirects from Redis in addition to Plone’s database

  2. REST API endpoints: Full CRUD operations for managing redirects via @redirects endpoint

  3. Control panel integration: Web UI for managing redirects (requires volto-redirector frontend)

  4. High-performance statistics: Redis pipelining for calculating statistics on 100k+ redirects in ~2 seconds

  5. Advanced search: Search both old and new URLs using simple or regex patterns (e.g., ^/publications, example.com)

  6. Fallback mechanism: Automatically checks Redis when redirects are not found in Plone storage

  7. Graceful error handling: Redis connection failures don’t break the redirection system

  8. API endpoint support: Intelligent hierarchical URL matching for API services and endpoints

  9. Proper HTTP status codes: Returns 410 Gone for permanently deleted resources (empty redirect targets)

  10. Redirect loop prevention: Built-in protection against circular redirects

  11. Easy configuration: Simple environment variable setup for Redis connections

  12. CSV import endpoint: Upload CSV files directly to the backend for bulk imports

  13. Non-intrusive: Extends existing Plone functionality without replacing it

Install

  • Via pip:

    $ pip install eea.api.redirector
  • Or via docker-compose:

    $ docker-compose up -d

This will start both Plone 6 and Redis services with the add-on pre-configured.

  • Install eea.api.redirector within Site Setup > Add-ons

Configuration

Redis connection settings are configured via environment variables:

  • REDIS_SERVER - Redis server hostname (default: localhost)

  • REDIS_PORT - Redis server port (default: 6379)

  • REDIS_DB - Redis database index (default: 0)

  • REDIS_TIMEOUT - Connection timeout in seconds (default: 5)

The included docker-compose.yml demonstrates how to configure these settings. The Plone service connects to Redis using:

environment:
  REDIS_SERVER: "redis"
  REDIS_PORT: "6379"
  REDIS_DB: "0"
  REDIS_TIMEOUT: "5"

How it works

The add-on extends Plone’s built-in plone.app.redirector by:

  1. Storage Integration: Adds a Redis storage utility alongside Plone’s database storage

  2. Fallback Lookup: When a redirect is not found in Plone’s database, it checks Redis

  3. API Support: Custom error handling for API endpoints with hierarchical URL matching

  4. Non-blocking: If Redis is unavailable, the system continues using Plone’s standard redirects

This design allows you to:

  • Manage redirects externally via Redis while maintaining Plone’s UI-based redirect management

  • Share redirects across multiple Plone instances using a common Redis server

  • Achieve faster redirect lookups for high-traffic sites

  • Store temporary or dynamic redirects that don’t need to persist in Plone’s database

REST API Endpoints

The add-on provides REST API endpoints for managing redirects:

GET /@redirects

List redirects with pagination and search support.

Query parameters:

  • q - Search query for old or new URL paths (supports regex: ^/publications, .*\.pdf$)

  • b_size - Batch size (default: 25, options: 10, 25, 50, 100, 500, 1000)

  • b_start - Batch start offset (default: 0)

  • search_scope - Where to search: old_url (default), new_url, or both

Example:

GET /Plone/@redirects?q=/themes&b_size=25&b_start=0&search_scope=old_url

Response:

{
  "@id": "http://localhost:8080/Plone/@redirects",
  "items": [
    {
      "path": "/old-path",
      "redirect-to": "/new-path"
    },
    {
      "path": "/deleted-page",
      "redirect-to": ""
    }
  ],
  "items_total": 187520
}

GET /@redirects-statistics

Get statistics for redirects.

Query parameters:

  • q - Optional search query to filter statistics

Example:

GET /Plone/@redirects-statistics?q=/themes

Response:

{
  "@id": "http://localhost:8080/Plone/@redirects-statistics",
  "statistics": {
    "total": 187520,
    "internal": 32935,
    "external": 1611,
    "gone": 152974
  }
}

Statistics categories:

  • total - Total number of redirects

  • internal - Redirects to internal paths (starting with /)

  • external - Redirects to external URLs (starting with http:// or https://)

  • gone - Empty redirects for permanently deleted content (HTTP 410)

POST /@redirects

Add new redirects.

Request body:

{
  "items": [
    {
      "path": "/old-path",
      "redirect-to": "/new-path"
    },
    {
      "path": "/deleted-page",
      "redirect-to": ""
    }
  ]
}

Notes:

  • Empty redirect-to marks content as permanently deleted (returns HTTP 410 Gone)

  • Paths are automatically trimmed of whitespace

  • Self-redirects are prevented (path cannot equal redirect-to)

Response:

{
  "success": 2,
  "failed": []
}

DELETE /@redirects

Remove redirects.

Request body:

{
  "items": [
    {"path": "/old-path"}
  ]
}

Response:

{
  "success": 1,
  "failed": []
}

POST /@redirects-import

Import redirects from a CSV file upload.

Request:

  • Multipart form-data with file field containing the CSV file

Example:

curl -u admin:admin -F "file=@redirects.csv" http://localhost:8080/Plone/@redirects-import #betterleaks:allow

Response:

{
  "type": "success",
  "success_count": 10,
  "failed_count": 0,
  "failed": []
}

Performance

The add-on uses Redis pipelining for optimal performance:

  • Statistics calculation: ~2 seconds for 187,000 redirects (42x faster than individual GET operations)

  • List pagination: Scans keys only, fetches values only for requested page

  • Search queries: Uses Redis SCAN with pattern matching for efficient filtering

Benchmark results (187,520 redirects):

  • SCAN all keys: 0.36s

  • Statistics with pipelining: 1.8s

  • Individual GET operations: 77s (not used)

HTTP 410 Gone Support

Redirects with empty targets (redirect-to: "") return HTTP 410 Gone status, indicating the resource has been permanently deleted. This is the proper HTTP status code for removed content that will not return.

The frontend (volto-redirector) can display a custom 410 Gone page with:

  • Information about the deleted resource

  • Link to Wayback Machine for archived versions

  • Helpful navigation options

Source code

Eggs repository

Plone versions

It has been developed and tested for Plone 6. See section above.

How to contribute

See the contribution guidelines (CONTRIBUTING.md).

Funding

EEA - European Environment Agency (EU)

Secret Scanning

This repository uses the Betterleaks GitHub Action to scan the current repository content on every push and pull request. The scan uses the rules in .gitleaks.toml and uploads a betterleaks-report artifact when a finding is detected.

If the optional SMTP secrets are configured, failed scans also send an email to the last commit committer. The workflow expects these repository or organization secrets:

  • SMTP_URL

  • SMTP_PORT (optional, defaults to 25)

  • SMTP_EMAIL

  • SMTP_PASSWORD (optional if the SMTP server does not require authentication)

Port 465 is sent with direct TLS; other ports use the default SMTP handshake. The email includes a short finding summary from the redacted Betterleaks report, including the redacted matched line from each finding.

There are three common outcomes:

  1. Everything is OK. The Betterleaks / Scan for secrets check is green and no action is needed. Regular references to runtime values are OK, for example:

    token_from_cookie = request.cookies.get("auth_token")
  2. A real secret was found. The check is red and the workflow log asks you to download the betterleaks-report artifact. Open the artifact from the GitHub Actions run and check the reported file, line and rule. Remove the committed value, move it to the proper secret store, and rotate it if it was exposed. A report entry looks like this:

    {
      "RuleID": "secret-literal-assignment",
      "File": "src/config.py",
      "StartLine": 12,
      "Secret": "[REDACTED]"
    }
  3. The finding is a false positive. Keep the value only if it is clearly not sensitive, such as a test fixture, placeholder, or public example. Add betterleaks:allow on the same line and include a short explanation in the pull request:

    test_password = "admin"  #betterleaks:allow

Do not add betterleaks:allow to real credentials.

Changelog

0.4 - (2026-07-13)

  • Change: fix: SonarQube report - refs #305404 [avoinea]

0.3 - (2026-04-14)

  • Change: Add unit tests for add_redirects validation logic [avoinea]

0.2 - (2025-12-20)

  • Feature: CSV import endpoint (@redirects-import) for server-side processing [avoinea - refs #293748]

0.1 - (2025-11-28)

  • Feature: Redis-backed URL redirects with high-performance lookups [avoinea - refs #293748]

  • Feature: REST API endpoints (@redirects, @redirects-statistics) for full CRUD operations [avoinea - refs #293748]

  • Feature: Advanced search with regex support and search scope selector (old_url, new_url, both) [avoinea - refs #293748]

  • Feature: HTTP 410 Gone support for permanently deleted content (empty redirect targets) [avoinea - refs #293748]

  • Feature: High-performance statistics using Redis pipelining (~2s for 100k+ redirects) [avoinea - refs #293748]

  • Feature: Pagination support with configurable batch sizes (10, 25, 50, 100, 500, 1000) [avoinea - refs #293748]

  • Feature: Alphabetical sorting of redirects for consistent display [avoinea - refs #293748]

  • Feature: Bulk import/export via REST API (CSV support in frontend) [avoinea - refs #293748]

  • Feature: Fallback mechanism to check Redis when redirects not found in Plone storage [avoinea - refs #293748]

  • Feature: Graceful error handling - Redis failures don’t break redirection system [avoinea - refs #293748]

  • Feature: API endpoint support with intelligent hierarchical URL matching [avoinea - refs #293748]

  • Feature: Redirect loop prevention [avoinea - refs #293748]

  • Feature: Easy configuration via environment variables (REDIS_SERVER, REDIS_PORT, REDIS_DB, REDIS_TIMEOUT) [avoinea - refs #293748]

Release files for eea.api.redirector 0.4

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for eea.api.redirector 0.4
File Size Uploaded
eea_api_redirector-0.4.tar.gz 36.1 kB Details

Release files / eea_api_redirector-0.4.tar.gz

Download URL eea_api_redirector-0.4.tar.gz
Size 36.1 kB
Tags Source
SHA-256 checksum
How to use checksums
d563e2fe5372bce5e1f424a7da63ecc18aacd6f12d386ef8cf0f2f67807a57dd
BLAKE2b-256 checksum
How to use checksums
0a2e264cafb5e1d892604bef33a70fa413d4148f7b1b8466ab6b670e8b73c9c6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.9.25

Release history Release notifications | RSS feed

This release

0.4 This release

1 release file

0.3

1 release file

0.2

1 release file

0.1

1 release file

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page