Skip to content

crazy-goat/go-mesi

Repository files navigation

mESI – Minimal Edge Side Includes Implementation in Golang

mESI (minimal Edge Side Includes) is a lightweight implementation of Edge Side Includes (ESI) in Golang, designed to add ESI support to multiple web servers. It provides basic but correct handling of the following ESI instructions:

  • <esi:include> – dynamic content inclusion,
  • <esi:remove> – removal of specified sections,
  • <esi:comment> – comments invisible to the end user,
  • <!--esi ... --> – inline ESI processing.

Features

  • Parallel Fetching – Unlike many other ESI implementations, mESI supports parallel fetching of ESI fragments, improving response times for dynamic content.
  • Lightweight & Minimal – Focuses on essential ESI features while remaining easy to integrate and extend.
  • Multi-Server Support – Can be integrated with various web servers to enhance content delivery performance.
  • A/B testing - Set fetch-mode="ab" to easily compare the effectiveness of two versions of content to see which one appeals more to people visiting web pages / viewing specific information.
  • Concurrent fetch - You need ultra performance - set fetch-mode="concurrent" to always fetch content from the fastest source.
  • esi:include timeout - Timeout can be set both globally and specifically for a selected esi:include tag. In combination with fallback content, you can easily manage the page generation time.
  • Fallback content - Set the content to be displayed if remote content download fails.
  • SSRF Protection – Built-in protection against Server-Side Request Forgery attacks with private IP blocking and optional host whitelisting.

See the full Feature Matrix for a detailed breakdown of which features are supported in each server integration.

ESI Parser Configuration

This document describes the configuration structure for the mESI parser.

Configuration Structure

The parser configuration is defined using the following structure:

type EsiParserConfig struct {
  DefaultUrl      string
  MaxDepth        uint
  Timeout         time.Duration
  ParseOnHeader   bool
  AllowedHosts    []string
  BlockPrivateIPs bool
  MaxResponseSize int64
}

Configuration Parameters

DefaultUrl

Base URL that will be used as a prefix for relative URL paths. If the provided URL in ESI tags doesn't start with "http://" or "https://", this base URL will be prepended to paths starting with "/".

MaxDepth

Defines the maximum allowed recursion depth for esi:include tags. This parameter prevents infinite loops that could occur when ESI templates reference each other. The recursion count value can be lowered for a selected esi:include tag using the max-depth attribute:

<esi:include max-depth="1" src="http://foo.bar/recursive"/>

Timeout

Specifies the maximum time to wait for a server response when processing esi:include tags. The request will be terminated if this timeout is exceeded. Timeout can also be defined independently in the esi:include tag. The timeout attribute value is given in seconds. Decimal values can be given, the decimal separator is a dot, e.g. 1.2

<esi:include timeout="0.2" src="http://foo.bar/some-long request" />

NOTE:

  • If the alt attribute is provided and first request fails the time budget will be split between both requests.
  • In case of recursion, the timeout value is reduced by the time it took to execute the previous step.
  • When a timeout value is set in both EsiParserConfig and esi:include, the smaller value will be chosen.

ParseOnHeader

If set to true, then server responses will process ESI tags only when the response contains the Edge-control: dca=esi header

BlockPrivateIPs

Protects against Server-Side Request Forgery (SSRF) attacks by blocking requests to private and reserved IP addresses. When enabled (default: true), the parser will reject ESI includes that resolve to:

  • Private networks: 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16
  • Loopback: 127.0.0.0/8, localhost
  • Link-local: 169.254.0.0/16
  • Unspecified: 0.0.0.0
  • Multicast: 224.0.0.0/4, 240.0.0.0/4

AllowedHosts

When set, specifies a whitelist of allowed hostnames for ESI includes. If defined, only requests to matching hosts will be allowed. Supports exact matches and subdomain matching (e.g., example.com matches www.example.com).

This is useful when you want to restrict ESI includes to a specific set of trusted domains while still blocking private IP ranges.

AllowPrivateIPsForAllowedHosts

When set to true, hosts in AllowedHosts are allowed to resolve to private/reserved IP addresses, bypassing the BlockPrivateIPs check.

⚠️ Security Warning: This creates a potential SSRF vector if an attacker can control DNS for a host in AllowedHosts. Only use in trusted environments where you control DNS resolution (e.g., internal reverse proxy setups).

Default: false (private IPs always blocked regardless of AllowedHosts)

Example:

config := mesi.EsiParserConfig{
    BlockPrivateIPs:                true,
    AllowedHosts:                   []string{"internal.local"},
    AllowPrivateIPsForAllowedHosts: true,
}

MaxResponseSize

Sets the maximum allowed size for HTTP response bodies (in bytes) when fetching ESI includes. Helps prevent OOM attacks from malicious or compromised upstream servers returning arbitrarily large responses.

  • Default: 10 * 1024 * 1024 (10 MB)
  • Set to 0 for unlimited (backward compatible)

When a response exceeds the limit, an error is returned with the message: response body exceeds maximum allowed size of X bytes

fetch-mode - esi:include tag only

Allows you to choose between three content download modes:

  • fallback - default way to download content. In case of an error downloading from the first location (src attribute), the content will be downloaded from an alternative address (alt attribute). The alt attribute is not mandatory. This mode is selected if the fetch-mode attribute is missing.
  • ab - Allows to download with different probability from two different locations src and alt. In case of no alt attribute, the src location will always be downloaded. The proportions of A and B are specified using the ab-ratio attribute, and it has the form X:Y where X and Y are positive integers. In case of no attribute or an incorrect value, the default proportion is 50:50.
  • concurrent - both locations are fetched at the same time, but the result is returned from the fastest location. If the alt attribute is missing, the same location will be called twice.

ab-ratio - esi:include tag only

Used only when fetching data when fetch-mode is set to ab. Specifies the proportion of fetches from src to alt. Given in the form X:Y, where X and Y are non-negative integers. For example, a value of 90:10 specifies that 10% of all queries will be fetched from alt source. In case of no attribute ab-ratio or an incorrect value, the default proportion is 50:50. If missing alt attribute, src will be always used.

<esi:include fetch-mode="ab" ab-ratio="90:10" src="http://foo.bar/A" alt="http://foo.bar/B" />]

I this case B will be fetched 10% of the time.

Fallback content

By default, the esi:include tag does not contain a body. mESI allows you to set the so-called fallback content, or the content that will be displayed in case the download of remote content fails. Example:

<esi:include src="https://foo.bar/bad-url">
    Failed to load remote content
</esi:include>

A fallback will be displayed both when we get an error in the server response or when the response time is too long. In combination with the timeout attribute, we can easily manage the page generation time. For example, if 250ms is exceeded, we can download this code fragment using FetchAPI or htmlx

<esi:include timeout="0.25" src="https://foo.bar/can-be-slow">
  <div hx-trigger="load" hx-swap="outerHTML" hx-get="https://foo.bar/can-be-slow"></div>
</esi:include>

Roadmap

Servers Integration

Initial Implementation – Basic support for ESI processing.
🔄 Upcoming Integrations:

Features

🔄 Performance & Scalability:

  • Implement include path without host
  • Add timeout parameter for ESI requests
  • Option to parse esi only when Edge-control: dca=esi header
  • Add work modes:
    • Fallback
    • A/B testing with ratio
    • Concurrent fetching
  • Add max concurrent request limit
  • Implement worker pool for optimized request handling
  • Debug mode - add a lot of debug messages

🔄 Caching Enhancements:

  • Add in memory cache
  • Add cache key option
  • Add Redis cache support
  • Add Memcached cache support

Running tests

To run E2E test just type make test-e2e

🚀 Looking for contributors! If you are interested in helping with development, feel free to submit PRs or open issues.

About

Lightweight ESI (Edge Side Includes) implementation in Go — parallel fetching, A/B testing, concurrent fetch, fallback content, Traefik/Nginx/Caddy plugins

Topics

Resources

License

Code of conduct

Stars

Watchers

Forks

Packages

 
 
 

Contributors