Fork me on GitHub

Symfony2 ESI error: File name too long

We recently came across a strange, intermittent error at OpenSky:

(36)File name too long: Cannot map GET /_internal… HTTP/1.1 to file

We use Varnish to cache large sections of our pages that include follow/unfollow buttons. If the user is following a certain curator we show the unfollow button, otherwise we show the follow button.

Our render tag looked like this:

{% render 'AppBundle:Curator:list' with
    { following: app.user.sortedFollowingSlugs }
    { standalone: true } %}

When the current user is following just a few curators this works fine. The app renders the curator list with follow/unfollow buttons accurate for the current user and Varnish caches it for us. However, when a user was following all or nearly all of our curators we see the “file name too long” error and Apache would render its 403 error template (no idea why it chooses 403 in this case).

Unpack the WTF

To understand why this happens we need to dig into how Symfony2 implements ESI. When ESI is enabled and your app sits behind a reverse proxy that supports ESI, any call to Twig’s {% render %} tag with the standalone option set to true will render an <esi:include /> tag instead of rendering the action inline. Included in the src attribute of that tag is an URL with all the values you passed to the {% render %} tag encoded in.

The routing pattern used for the ESI URL looks like this:


In the case above the {controller} value would be AppBundle:Curator:list and the {path} value would be equivalent to the following:

php > echo http_build_query(array('following' => array('bar', 'foo')));

Now imagine what the {path} value will look like when a user is following 100+ curators. Big, right? Turns out it would be too big for Apache to handle as a filename, which cannot exceed 255 characters, hence the “file name too long” error.

Query String to the Rescue!

Fortunately the fix is easy and does not require any hacking on Symfony. By moving the potentially large parameter to the ESI URL’s query string our character limit jumps from 255 up to 8,190 — lots more room!

{% render 'AppBundle:Curator:list' {
    standalone: true,
    query: { following: app.user.sortedFollowingSlugs }
} %}

Of course you would also need to update CuratorController::listAction() to look for the following parameter in the query string rather than in the action arguments, but that’s an easy change.

Stay Curious

Symfony makes a lot of cool things very easy for you, but don’t become lazy. Open up the source code and figure out how it works, what technologies you’re using under the hood, and what constraints may be lurking in a dark corner somewhere, waiting to pounce.