How to enable gzip and Brotli compression in WordPress
Text compression is switched on at the web server or at a CDN in front of it. Check first, with curl or the browser's network panel, because it may already be on. If it is not, turn it on at the host, in a caching plugin, in .htaccess on Apache, or in nginx's configuration.
- By
- WP Ministry
- Updated
In short
- Compression is done by the web server or by a CDN in front of it. Check before you change anything.
- Look for a content-encoding header on the page, and on a stylesheet or script separately.
- If a CDN or proxy answers for the site, it decides the compression. A rule on your server may change nothing a visitor sees.
- On Apache the rules go in .htaccess, outside WordPress's own block, wrapped so that a missing module cannot take the site down.
- Compress text such as HTML, CSS, JavaScript, JSON, XML and SVG. Leave images, video and WOFF2 fonts alone.
- Brotli compresses further than gzip, but the host or CDN has to support it. Keep gzip as the fallback.
Text compression makes the server shrink a page's HTML, CSS and JavaScript before sending them, and the browser unpacks them when they arrive. gzip is the common method and Brotli is the newer one. It is switched on at the web server, or at a CDN in front of it, and some caching plugins can do it for you.
Check before you change anything, because it may already be on. If it is not, there are four places to turn it on, depending on what you can reach: your host, a caching plugin, .htaccess on Apache, or the server's configuration on nginx.
How it works, in three headers
Accept-Encodingis sent by the browser. It lists the methods the browser can unpack.Content-Encodingis sent back by the server. It names the method the server used:gzip,brfor Brotli, orzstdfor Zstandard, a newer one. This is the header you look for. If it is missing, the response was not compressed.Vary: Accept-Encodingtells any cache on the way to keep the compressed and uncompressed copies apart, so that a compressed copy goes only to a browser that asked for one.
These methods are built for text. MDN's guide to HTTP compression puts the reduction at up to 70% for some documents, and nginx's manual says gzip often cuts the data sent by half or more.
Check whether compression is already on
Check the page and one of its files separately. The page is built by WordPress, and a stylesheet or script is a file read from disk. A server can compress one and not the other, and the files may come from a different server altogether.
With curl
This asks for the home page in any compressed form your copy of curl supports and prints the header that matters. Use the address exactly as your browser shows it, with https:// and with or without www. If the address redirects, you are reading the redirect's headers and not the page's.
curl -s -o /dev/null -D - --compressed https://example.com/ | grep -i "content-encoding"--compressed requests a compressed response. -D - prints the response headers, -o /dev/null throws the page itself away, and -s hides the progress meter. On Windows, write -o nul, leave off everything from the | onward, and read the whole list of headers.
A line such as content-encoding: gzip or content-encoding: br means the page is compressed. No output means the response came back uncompressed. To see every header that came back, run the command again without the | grep part.
Now do the same for a stylesheet or a script. View the source of a page, copy an address that ends in .css or .js, and put it in place of the one below.
curl -s -o /dev/null -D - --compressed https://example.com/wp-content/themes/example-theme/style.css | grep -i "content-encoding"To ask for one method only, replace --compressed with -H "Accept-Encoding: gzip" or -H "Accept-Encoding: br".
In the browser
These steps are for Chrome.
Step 1: Open the developer tools on the Network tab
Press Control+Shift+J, or Command+Option+J on a Mac, then click Network. Reload the page.
Step 2: Select a request
The first request in the list is the page itself. Click it, then open the Headers tab.
Step 3: Read the response headers
Under Response Headers, look for
content-encoding. Then do the same for a request that ends in.cssor.js.Step 4: Compare the sizes
In the Network panel's settings, switch on Big request rows. The Size column then shows two figures for each request, and the bottom one is the uncompressed size.
What a Lighthouse report shows
Lighthouse is the tool behind PageSpeed Insights. Since Lighthouse 13 its report checks compression inside the "Document request latency" insight, on a line that reads "Applies text compression" or "No compression applied". That line is about the page's own HTML and nothing else, so a report can pass while stylesheets and scripts are sent uncompressed. Check those with the curl command above.
Older reports and guides use the name "Enable text compression". That audit gathered every text response that arrived with no content-encoding of br, gzip or deflate. It left out any response smaller than 1.4 KiB, and any that compression would shrink by less than 10%.
Find out who is answering
If a CDN or a proxy sits in front of the site, the header you just read is its header, not your server's. Cloudflare's documentation says it compresses what it sends to visitors with gzip, Brotli or Zstandard, depending on the plan and on what the browser accepts. It also says it can take an uncompressed response from your server and send it on compressed, or the other way round.
So on a site behind such a service, the setting to change is in that service's dashboard, and a rule added on your server may change nothing a visitor sees. How to set up a CDN for WordPress shows how to tell from the headers whether the CDN answered.
Where to switch it on
These are in order of who can do it. Stop at the first one that works, then run the check again.
Your host
A host can switch compression on for every site on a server, so ask support before you edit anything. On managed hosting that runs nginx there is no .htaccess file to edit, and support is the only route.
Some control panels have a setting of their own. cPanel's is called Optimize Website. Its documentation says it compresses the kinds of content you specify and saves the setting to an .htaccess file in your home directory. Choose "Compress the specified MIME types" and enter the text types listed further down, then press Update Settings. The screen appears only when the host has enabled Apache's mod_deflate module.
A caching plugin you already run
Several of the plugins in WordPress caching plugins compared can turn compression on.
- WP Rocket adds rules to
.htaccesswhen it is activated, on Apache and LiteSpeed servers. One block of them is for gzip, and its documentation says that block "is used to compress the assets of the site". - WP Fastest Cache has a Gzip setting. Its listing says that because nginx does not use an
.htaccessfile, you enable Gzip by hand there. - WP Super Cache lists "Compress pages" among its recommended settings. Its listing says the cached files are compressed once and stored that way.
Do not install a caching plugin for this alone, and do not run two.
Rules of your own, on Apache or nginx
.htaccess
<IfModule mod_deflate.c>
<IfModule mod_filter.c>
AddOutputFilterByType DEFLATE text/html text/plain text/css text/xml
AddOutputFilterByType DEFLATE text/javascript application/javascript application/json
AddOutputFilterByType DEFLATE application/xml application/rss+xml application/atom+xml
AddOutputFilterByType DEFLATE image/svg+xml font/ttf font/otf application/vnd.ms-fontobject
</IfModule>
</IfModule>On Apache, put the block above the line # BEGIN WordPress. WordPress writes the lines between # BEGIN WordPress and # END WordPress itself, and the note it leaves there says changes between the markers will be overwritten.
- The two
<IfModule>lines make Apache skip the rules when a module is not loaded.mod_deflatedoes the compressing, andmod_filterprovides theAddOutputFilterByTypedirective. Without the wrapper, a missing module would be a line the server cannot read. mod_deflatesends theVary: Accept-Encodingheader itself. It produces gzip only.
On nginx, .htaccess is not read, so this needs access to the server's configuration.
gzip oncompressestext/htmland nothing else. nginx always compresses that type, which is why it is not in the list.gzip_typesadds the others.gzip_vary onadds theVary: Accept-Encodingheader.gzip_proxied anymatters behind a CDN or proxy. By default nginx does not compress the answer to a request that came through a proxy, which it recognizes by the request'sViaheader.gzip_min_length 1000skips responses too small to be worth it. The figure is the one in nginx's own example. The default is 20 bytes.
Then check the file and load it:
nginx -t
nginx -s reloadnginx -t checks the configuration for correct syntax. If a reload is given a configuration it cannot apply, nginx's documentation says it rolls back and keeps working with the old one.
To undo either, remove the lines you added. On nginx, reload again.
What to compress and what to leave alone
Compress the text types:
- HTML, CSS and JavaScript
- JSON, XML and feeds
- SVG images, which are text
- Fonts in the older TTF, OTF and EOT formats. Cloudflare's list of the types it compresses includes all three.
Leave these out:
- Images such as JPEG, PNG and GIF, audio and video. MDN's guide recommends compression for all files except those already compressed, and names images, audio and video. Compressing them a second time gains nothing and can make the file larger.
- WOFF and WOFF2 fonts. web.dev notes that both have compression built in, and that WOFF2's is Brotli.
That is the reason to list types, and not to pick a setting that compresses everything. Making images smaller is a different job: see how to optimize WordPress images without losing quality.
Brotli
Brotli is a newer method, sent as Content-Encoding: br. MDN says it gives better compression ratios than gzip. It also says Brotli is slower to compress, so gzip may be the better choice for content that cannot be cached.
Two things decide whether you can use it.
- The site must be on https. Chrome's own entry for the feature is titled "Accept-encoding: br on HTTPS connection", and Mozilla's announcement says Firefox supports Brotli over HTTPS but not HTTP.
- The server or CDN must support it. On Apache it needs the
mod_brotlimodule, available from version 2.4.26. On nginx it needs ngx_brotli, a module from Google's own repository that has to be compiled into nginx or loaded as a dynamic module. On shared or managed hosting, both are the host's to provide. Behind a CDN that compresses with Brotli, you may need nothing on the server at all.
.htaccess
<IfModule mod_brotli.c>
<IfModule mod_filter.c>
AddOutputFilterByType BROTLI_COMPRESS text/html text/plain text/css text/xml
AddOutputFilterByType BROTLI_COMPRESS text/javascript application/javascript application/json
AddOutputFilterByType BROTLI_COMPRESS application/xml image/svg+xml
</IfModule>
</IfModule>Chrome's documentation advises using gzip as the fallback to Brotli, for a browser that does not ask for br. To check, run the curl command with -H "Accept-Encoding: br" and look for content-encoding: br.
I added the rule and nothing changed
- The module is not loaded. On Apache, the
<IfModule>wrapper skips the rules without a word. Ask the host whethermod_deflateis enabled. On your own server,apachectl -Mlists the loaded modules. - A CDN or proxy is answering. Its setting decides what visitors get. See "Find out who is answering" above. On nginx behind a proxy, check
gzip_proxied. - The response is too small. nginx skips anything under
gzip_min_length, and it reads that length only from the response'sContent-Lengthheader. Cloudflare does not compress a response under 48 bytes with gzip, or under 50 with Brotli. Check with a real page or a full stylesheet. - The content type is not in the list. The server matches on the response's
content-typeheader, not on the file's ending. Read that header on the uncompressed response and add exactly that type. Scripts, for one, may be sent astext/javascriptor asapplication/javascript. - A cached copy is being served. A page cache or a CDN may still hold copies made before the change. Clear the caching plugin's cache, then the host's and the CDN's, and check again with curl.
- The rule is in the wrong place.
.htaccessbelongs in the folder the site is served from, and nginx does not read it at all. On nginx, a change does nothing until the configuration is reloaded. - The page turned into garbage characters. It is being compressed twice. WP Super Cache's listing gives this cause: compression switched on in the plugin when the web server, or PHP's
zlib.output_compressionsetting, already does it. Switch one of them off.
If every page shows an error after you saved .htaccess, remove the lines you added. How to fix the 500 internal server error has the steps for getting back to a working file.
What not to rely on
- Compression does not make the server answer sooner. It shrinks what is sent. The time WordPress takes to build the page is unchanged.
- It does nothing for images. They are compressed already, and an image that is too large stays too large.
- A clean report is not the goal. A current Lighthouse report checks only the page's own HTML, and the older audit ignored small responses on purpose.
Compression is one item on a longer list. How to speed up WordPress puts the rest in order. If you would rather hand the job over, our speed optimization service is a one-time job: you name the pages, and we measure them before the work and again after it.
Common questions
Do I need a plugin to enable gzip in WordPress?
No. Compression is done by the web server or by a CDN, and it may be on already. A caching plugin is one way to add the server rules if you already use one. Check first, then ask the host.
Should I use gzip or Brotli?
Both, where you can. Serve Brotli to browsers that ask for it and gzip to the rest. If your host offers only gzip, use gzip. The check is the same either way: a content-encoding header on the page and on its stylesheets and scripts.
Why is the page compressed but not the stylesheets and scripts?
Either the server's list of types leaves them out, or they come from somewhere else. On nginx, gzip on with no gzip_types line compresses HTML only. If the files are served from a CDN's hostname, that CDN's setting is the one to change.
Does compression put more load on the server?
Some. Compressing takes processor time, and both Apache's and nginx's manuals describe a compression level from 1 to 9. Apache's says a higher level compresses better and takes more processor time. Leave the level at its default unless you have measured a reason to change it.

