Skip to content
EnterraHost
Products
Tools
Hosting Email Support Client Area Find a domain

Enable Brotli compression for PHP output on your site

Brotli compression cuts the HTML your site sends to a fraction of its size. On a plain page it is worth more than every other delivery change combined, because HTML is the one file every visitor downloads on every visit.

It takes two things. The extension has to be enabled, and its output switch has to be on. Miss either one and PHP says nothing, which is the part that catches people.

Why brotli compression does nothing until both switches are on

PHP ignores a directive whose extension is not loaded. It does not warn. It does not fail, and it writes no log line. Ask PHP for the value and it comes back empty.

So brotli.output_compression = On in a configuration where Brotli was never enabled, is a setting that looks applied and does nothing at all. Both need to be true before a single byte is compressed.

Turning it on

The three dot menu at the right of a website row, open, showing Manage, Disable and Delete as the three options.

Sign in, choose Websites in the left menu, and open Manage websites. Find your domain, open the three dot menu at the right of its row, and choose Manage.

In the top bar, select Advanced.

The hosting plans each include a website on one of the PHP versions listed on this page. The version decides which Brotli build is loaded, and not whether this works, because a build exists for every version we offer.

The Configurable extensions section on the PHP settings page. A row of tick boxes under a Configurable extensions heading, with apcu and brotli ticked, and oauth, pdo_dblib, pdo_pgsql, pgsql and xmlrpc left unticked. Brotli is the second box in the top row.

Start with the extension. On the PHP panel the configurable extensions sit below the built in list. Tick brotli. This is the step people skip, and it is the step that makes the next one work.

The php.ini editor showing two directives in a table headed DIRECTIVE and SETTINGS. The first row reads brotli.output_compression followed by a dark Inherited badge, with ENABLED shown in green beside it. The second row reads opcache.enable with a value of 1. An Add directive button sits above the table.

Then open the php.ini editor, which sits in the same part of the dashboard, and choose Add directive. Enter brotli.output_compression as the directive, and set the value to enabled. The panel shows it sitting beside opcache.enable, which is the same mechanism doing the same job for a different extension.

A setting you did not add yourself shows a grey Inherited tag. That means it came from the server rather than from this site, and it can be edited here but not deleted. If Brotli already reads as enabled without you touching it, this site is inheriting it and there is nothing to add.

Restart PHP from the same panel. The extension and the directive are both read when the PHP process starts, so neither takes effect until it has.

The part Brotli cannot reach

PHP compresses what PHP produces. A stylesheet is not produced by PHP. Apache reads it off disk and sends it, so the PHP setting never sees it, and your CSS and JavaScript stay uncompressed no matter what you tick.

That is what the next section is for. It is compressed at the Apache layer instead, using gzip, and the two settings do not overlap.

Adding the security headers and gzip to .htaccess

A .htaccess file is just a text file in public_html, so any editor you can reach the server with will do. The Files tab on the website dashboard edits files in the browser and needs no client at all. An FTP client such as FileZilla works too, and so does a local editor connected over SSH, which is the one to use if you keep the file in version control. The article on creating an SSH key covers the SSH route and how to connect once a key is on the account.

In public_html, open .htaccess and add the following. It is a dot file, so most FTP clients hide it until you turn on showing hidden files, and the file may not exist yet on a site that has never had one. The gzip block compresses CSS and JavaScript. The header block sets the security headers a scanner will look for.

Every block is wrapped in an IfModule test on purpose. A bare Header or AddOutputFilterByType line on a server missing that module is a 500 for the entire site, which turns a hardening step into an outage.

<IfModule mod_deflate.c>
    AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css
    AddOutputFilterByType DEFLATE text/javascript application/javascript
    AddOutputFilterByType DEFLATE application/json application/ld+json
    AddOutputFilterByType DEFLATE application/xml image/svg+xml
    SetEnvIfNoCase Request_URI \.(?:gif|jpe?g|png|webp|avif|ico|zip|gz|mp4|woff2?)$ no-gzip dont-vary
</IfModule>

<IfModule mod_headers.c>
    Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
    Header always set X-Content-Type-Options "nosniff"
    Header always set X-Frame-Options "SAMEORIGIN"
    Header always set Referrer-Policy "strict-origin-when-cross-origin"
    Header always set Permissions-Policy "camera=(), microphone=(), geolocation=()"
</IfModule>

Two of those deserve a warning before you paste them.

includeSubDomains on HSTS binds every subdomain to HTTPS for a year. If you have a subdomain that only answers on HTTP, a mail host or an old application, browsers will refuse to load it and the visitor cannot click through. Remove that flag if you are not certain every subdomain has a certificate.

A strict security policy can break a page silently. The longer cousin of these headers is a Content-Security-Policy, and a wrong one blocks your scripts and images. Start any policy in report-only mode if you add one later, read what it reports, and only then make it enforcing. Nothing above is a content policy, so the block is safe to apply as written.

Checking that both actually took effect

Do not trust the panel. A setting that shows as enabled and a response that carries an encoding are two different facts, and only the second one matters.

The Server Check tool answers it without a terminal. Enter the domain, tick Show response headers, and run the check. It reports what the server sends, including the Content-Encoding header on the page it fetched, whether HSTS and CSP are present, which TLS version and issuer the certificate uses, how many days it has left, the total request time with the TLS handshake broken out separately, and whether IPv4 and IPv6 both answer.

That header list is the fastest way to see whether the .htaccess block landed. If HSTS, X-Frame-Options and the rest appear there, the block is being read. If none of them do, it is not, and the file placement or the server is the place to look.

One thing the tool cannot tell you. If the site is behind a CDN, the request never reaches your server and the numbers describe the edge, not the origin.

A terminal answers the compression question more precisely, because it lets you ask for a specific encoding. That is the one case where curl beats the tool.

Ask for the page with Brotli offered, and read the Content-Encoding header back.

curl -sI -H "Accept-Encoding: br" https://example.com/ | grep -i content-encoding

You want br. If nothing comes back, the page is being served uncompressed, and the usual cause is the extension not being enabled in the PHP panel. The directive is rarely the wrong part.

Then check a static file, which should come back as gzip from Apache rather than Brotli from PHP.

curl -sI -H "Accept-Encoding: gzip" https://example.com/wp-content/themes/your-theme/style.css | grep -i content-encoding

A gzip here means the .htaccess block is working. An empty result means the file is being sent at full size and the block did not apply. Check them in that order, because the two layers fail independently.

Compare the sizes rather than only the header. A request with Accept-Encoding: identity returns the uncompressed length, and the difference tells you what you actually gained.

One thing to leave alone. If you install a caching plugin, do not let it compress what PHP already compresses. Two compressors on one response produce a body the browser reads as broken, and it looks like the host is at fault.

Summarize this guide with

You need an account with the assistant you choose. It fetches the guide itself, and you send the prompt, not us.

Every guide in this category is on the Shared Web Hosting page.