Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Webperf: boost your PHP apps with 103 Early Hints

Webperf: boost your PHP apps with 103 Early Hints

Google Chrome, Firefox, Caddy, and Apache now support a new HTTP status code: 103 Early Hints. Since version 6.3, Symfony also supports 103 responses, and FrankenPHP also supports it natively.

This new, rather unusual type of response has a single objective: to speed up the loading time of your websites and applications by allowing the browser to preload the resources it will need to render the page.

In this presentation, we'll look at how 103 differs from other preloading techniques such as HTTP/2+ Server Push or tags, and how to take advantage of it from within our PHP applications.

Kévin Dunglas

October 13, 2023
Tweet

More Decks by Kévin Dunglas

Other Decks in Programming

Transcript

  1. 103 Early Hints
    A new HTTP status code to improve
    webapp performance
    Photo by Javier Allegue Barros

    View full-size slide

  2. Kévin Dunglas
    ➔ Co-founder of Les-Tilleuls.coop
    ➔ Creator of API Platform and FrankenPHP
    ➔ Contributor to PHP, Go and Caddy server
    @dunglas

    View full-size slide

  3. 70+ API, Web and Cloud Experts
    ➔ 100% employee-owned co-op ✊
    ➔ API Platform creators 🕷
    ➔ PHP Foundation advisory board 🐘
    ➔ Symfony backers and core team 🎼
    ➔ Laravel official partners 🏎
    [email protected] 💌

    View full-size slide

  4. Typical
    Web Page
    © Google

    View full-size slide

  5. Typical Web Page
    GET /index.html
    Host: example.com
    [server think time]
    200 OK
    Content-Type: text/html; charset=UTF-8
    [... rest of the headers ...]


    [... rest of the response body ...]
    1st request:
    1st response:

    View full-size slide

  6. Typical Web Page, continued
    GET /common.css
    Host: example.com
    200 OK
    Content-Type: text/css; charset=UTF-8
    [... rest of the headers ...]
    body { /* … */ }
    2nd client request:
    2nd response:

    View full-size slide

  7. 1XX Informational
    ➔ Informational HTTP responses: 1XX
    ➔ “Temporary” responses sent before
    the final response
    ➔ In HTTP since forever
    ➔ Mostly used for “internal” things
    handled directly by the browser and
    servers like file upload (100 Continue)
    Photo by Possessed Photography

    View full-size slide

  8. 103 Early Hints
    Photo by Possessed Photography
    ➔ 103: new HTTP status code for indicating
    hints
    ➔ Fetch external resources as early as
    possible
    ● E.g.: CSS and JS files, images…
    needed to render a web page
    ➔ Goal: minimize perceived latency
    ➔ Rely on pre-existing standards:
    ● Link header (RFC 5988)
    ● Resource hints (HTML standard)
    ● Content Security Policy
    ➔ HTTP/2 and HTTP/3 connections only

    View full-size slide

  9. 103 Early
    Hints
    © Google

    View full-size slide

  10. Web Page Using Early Hints
    GET /index.html
    Host: example.com
    103 Early Hints
    Link: ; rel=preload;
    as=style
    [server think time]
    200 OK
    [... typical headers ...]
    Link: ; rel=preload;
    as=style

    [... rest of the response body ...]
    GET /common.css
    Host: example.com
    200 OK
    Content-Type: text/css; charset=UTF-8
    [... rest of the headers ...]
    body { /* … */ }

    View full-size slide

  11. Benefits: ~30% LCP improvement
    © Google

    View full-size slide

  12. What About HTTP/2 Server Push?!
    ➔ Server Push served the same purpose
    ➔ Server Push has been removed from
    Chrome
    ● Complex
    ● Rarely used in the wild
    ● “Over-pushing”: only 40% of pushed
    requests were used
    ➔ 103 Early Hints requires one more
    Round-trip time (RTT) but…
    ● “Simple”
    ● Better cache dynamics: if the
    response is cached, it is not
    downloaded again

    View full-size slide

  13. Using Early Hints

    View full-size slide

  14. Browser Support
    ➔ Stable:
    ● Chrome
    ● Edge
    ● Opera
    ➔ Experimental (not enabled by default):
    ● Firefox
    ● Safari
    ➔ Graceful fallback on “preload” links
    ● All major browsers
    Photo by Andrew Robulack

    View full-size slide

  15. Caddy 😍
    @hint {
    protocol http2+
    path /index.html
    }
    route @hint {
    header Link "; rel=preload; as=style"
    respond 103
    }

    View full-size slide

  16. Apache
    H2EarlyHints on
    # If enabled, HTTP/2 Server Push will also be used
    H2Push Off

    H2PushResource /common.css

    View full-size slide

  17. Programmatically
    With PHP

    View full-size slide

  18. Houston, we have a problem

    View full-size slide

  19. PHP FPM Internals
    Web Browser Web Server
    (NGINX, Caddy, …)
    HTTP
    REQUEST
    HTTP
    RESPONSE
    PHP-FPM
    FASTCGI
    REQUEST
    FASTCGI
    RESPONSE
    ➔ FastCGI doesn’t support informational
    responses
    ➔ 1 FastCGI request = only 1 response

    View full-size slide

  20. A hack?
    header('HTTP/1.1 103 Early Hints');
    header('Link: ; rel=preload; as=style');
    // Send the informational request and craft the final one “by
    hand”
    echo "HTTP/1.1 200 OK\r\n";
    echo "Link: ; rel=preload; as=style\r\n";
    // Other headers
    echo "\r\n";
    echo "Body";

    View full-size slide

  21. Not a Reliable Hack
    Photo by Joshua Hoehne
    ➔ Complex, but works as intended if the web
    server serves the response over HTTP/1.1
    ➔ Broken if the web server serves the response
    over HTTP/2 or HTTP/3
    ● HTTP/2+ is a binary protocol
    ● Only one response is sent
    ● The handcrafted HTTP/1.1 text of the second
    response is included “as-is” in the response
    body of the first response
    ➔ A hack on top of a hack:
    ● Force the web server to use HTTP/1.1
    ● Add an HTTP/2+ reverse proxy in front

    View full-size slide

  22. A Better Solution

    View full-size slide

  23. FrankenPHP Internals
    ➔ PHP is plugged directly into the web server
    ➔ Send as many HTTP responses as you want
    Web
    Browser
    HTTP
    REQUEST
    HTTP
    RESPONSE

    View full-size slide

  24. Early Hints with FrankenPHP
    // Start FrankenPHP: frankenphp php-server
    header('Link: ; rel=preload; as=style');
    headers_send(103);
    // your slow algorithms and SQL queries 🤪
    echo 'Body';

    View full-size slide

  25. With Laravel
    $r = new Response();
    $r->headers->set('Link', '; rel=preload; as=style');
    $r->sendHeaders(103);
    $r->setContent('My final response');
    (and other tools using Symfony HttpFoundation)

    View full-size slide

  26. With Symfony Full Stack
    class HomepageController extends AbstractController
    {
    #[Route("/", name: "homepage")]
    public function index(): Response
    {
    $response = $this->sendEarlyHints([
    (new Link(href: '/common.css'))->withAttribute('as', 'stylesheet'),
    ]);
    // prepare the contents of the response...
    return $this->render('homepage/index.html.twig', response: $response);
    }
    }

    View full-size slide

  27. Thank you!
    ➔ Any questions ?
    les-tilleuls.coop
    [email protected]
    @coopTilleuls

    View full-size slide