How to make your Liferay site fast
Measure, fix how pages are delivered, then tune the JVM, Liferay and Elasticsearch. Part two of the Liferay series.
Liferay's own go-live checklist is mostly about the server: garbage collection, thread pools, caches. That tuning matters, but a large share of what visitors feel is decided before a request reaches Tomcat: whether responses are compressed, whether the browser and a CDN can keep what they already downloaded, and how heavy the page is.
This journey does both, in the order that pays back fastest. You start by measuring. Then you fix delivery, where an auditor can confirm each change on your monitored URL. Then you tune the JVM, Liferay and Elasticsearch, using the starting values from Liferay's checklist. The last step measures again.
The server tuning steps complete by hand, because nothing outside your network can see a heap size or a thread pool. Treat the numbers in them as starting points. Liferay says the same: test under a load that looks like yours before you settle on them.
This is the second of three Liferay journeys. The others are How to secure your Liferay site and How to keep your Liferay site online.
Steps
Open a step to read it1
Measure your starting pointVerifies with Time To First Byte AuditorTime to first byte is how long the server takes to start answering. On Liferay it is the number that server tuning moves: page rendering, database, search and caches all sit inside it.
The Time To First Byte Auditor measures the monitored URL five times and averages. Up to 800 ms passes, up to 1800 ms is a warning, above that fails.
Do this
Run the auditor now, before changing anything
Write the average down
Run it on a heavy page too, such as a search or listing page, if you monitor one
Done when you have a number to compare against. Mark the step complete whatever the result is. The last step of this journey runs the same auditor again.
2
Run the latest fix release of your lineVerifies with Latest Version AuditorFix releases carry performance fixes as well as security ones: a slow query, a cache that misses, a memory leak that another customer found in production. Running an old fix release means tuning around problems Liferay has already solved.
The Latest Version auditor compares the version in your project's Software Stack with the release list Liferay publishes. It fails when a later fix release of your line exists, and warns when a newer line exists at all.
Before you run it
The project's Software Stack needs a
Liferayentry with the product and the version that is running. Pick them from the list the form offers
Do this
Run the auditor. A failure names the fix release you should be on
Apply it before you tune. A fix release stays inside your line, so the update is small, and it changes the baseline you measured in step 1
After each update, change the version in the Software Stack and run again
Done when the auditor reports no later fix release of your line. The warning about a newer line is about support, not speed; How to keep your Liferay site online covers it. That warning stays while you are on an LTS line, so this step completes by hand.
3
Turn off developer settingsVerifies with Minifier AuditorSettings that make development comfortable make production slow: files checked for changes on every request, templates recompiled, CSS and JavaScript served as separate unminified files.
The Minifier Auditor fetches the page and its stylesheets and scripts and fails each one that is not minified. On Liferay, unminified CSS or JavaScript usually means developer mode is still on.
Do this
Make sure the server does not load
portal-developer.propertiesKeep
theme.css.fast.load=trueandjavascript.fast.load=true, which are the defaultsKeep
direct.servlet.context.reload=falseIn Tomcat's JSP servlet, turn off development mode so it stops checking JSP files for changes
In System Settings, raise the FreeMarker template Resource Modification Check Interval from 60 ms to ten minutes or more
Minify the CSS and JavaScript of your own themes, fragments and client extensions at build time
Done when the auditor reports your stylesheets and scripts as minified. It also checks the HTML itself, and Liferay's go-live checklist advises turning off the Strip filter that removes whitespace from HTML, so a finding about the page HTML can remain. Compression in the next step takes care of most of that whitespace. This step completes by hand for that reason.
4
Compress responsesVerifies with Content Encoding AuditorLiferay pages are large HTML documents and compress very well. Sending them uncompressed is the cheapest performance problem to fix.
The Content Encoding Auditor requests the page the way a browser does and fails when the response is not compressed with gzip, Brotli or zstd.
Do this
Compress at the web server or load balancer, not in Liferay. Liferay recommends moving this work off the application server so it spends its CPU on rendering pages
Apache:
mod_deflateormod_brotli. Nginx:gzip onwithgzip_typesfor HTML, CSS, JavaScript, JSON and SVGLeave
com.liferay.portal.servlet.filters.gzip.GZipFilteroff once the web server compresses, so nothing is compressed twiceIf a CDN sits in front, check that it passes compressed responses through or compresses them itself
Done when the auditor reports
gzip,brorzstd.5
Serve over HTTP/2 or HTTP/3Verifies with HTTP Version AuditorA Liferay page loads dozens of files. Over HTTP/1.1 the browser fetches them a few at a time. HTTP/2 sends them over one connection in parallel, and HTTP/3 does the same without stalling on a lost packet.
The HTTP Version Auditor passes when the site advertises HTTP/3, warns on HTTP/2 and fails on HTTP/1.x alone.
Do this
Turn on HTTP/2 where TLS ends: the web server, the load balancer or the CDN. Tomcat behind it can keep speaking HTTP/1.1 or AJP
Turn on HTTP/3 if that layer supports it. Most CDNs do with one switch
Done when the site answers over HTTP/2 at least. The auditor still warns at HTTP/2, so this step completes by hand. HTTP/2 is a reasonable place to stop when your web server does not offer HTTP/3.
6
Cache what does not changeVerifies with Cache AuditorEvery file a browser or CDN can keep is a request Liferay never sees. Caching depends on response headers, and one wrong header on a busy file type sends all of that traffic to the server.
The Cache Auditor loads the page, then reads the cache headers of the page and of every image, script, stylesheet, video and frame on it. It has one rule for Liferay: a file under
/documents/(Documents and Media) whoseCache-Controllackspublicfails, because a CDN or shared cache will not keep it and every image or PDF download reaches Liferay.Do this
Run the auditor and group the findings by URL pattern. One fix usually clears many
For Documents and Media files that guests can see, send
Cache-Control: publicwith amax-age. Recent Liferay releases have a cache setting for Documents and Media in System Settings. On older ones, set the header at the web server for/documents/pathsNever mark private documents public. Apply the header only to files and folders that guests may view
Give static files a
Cache-Controland anETag. RemovePragmaheadersIf you use Cloudflare, CloudFront or Imperva, the auditor also fails resources the CDN keeps passing to the origin. Check the CDN's cache rules for those paths
Done when the auditor passes. Warnings keep this step from completing by itself. Read them, fix what applies, and mark the step complete if the rest is accepted.
7
Put static assets on a CDNVerifies with CDN Coverage AuditorA CDN serves files from a location near the visitor and takes that traffic off your servers. For a site with visitors in more than one region, it is the largest single improvement in load time.
The CDN Coverage Auditor checks the page and its stylesheets, scripts, images, fonts and video, and reports which ones are served by a CDN. All of them on a CDN passes, some is a warning, none fails.
Do this
The simplest setup puts the CDN in front of the whole site, as a reverse proxy. Everything is covered and the cache headers from the previous step decide what it keeps
The alternative is a CDN host for static files only. Set
cdn.host.httpsinportal-ext.properties, or the CDN host in Instance SettingsWith a CDN host, leave
cdn.dynamic.resources.enabled=trueunless your CDN cannot fetch files from the origin on demandCheck that Documents and Media files are covered too. They are often the heaviest files on the page
Done when the auditor reports every resource as served by a CDN.
8
Trim heavy assetsVerifies with Large Resource AuditorOne oversized image undoes the rest of this journey for whoever loads that page. On Liferay it usually arrives through content: an editor uploads a photo straight from a camera and places it on the home page.
The Large Resource Auditor loads the page and fails every image, script, stylesheet, video or frame of 512000 bytes or more. You can change the limit in the auditor's
sizeLimitpreference on the URL's Configuration tab.Do this
Turn on Adaptive Media and create image resolutions, so Liferay serves a size that fits the screen instead of the original upload
Re-export the images the auditor lists, in WebP or AVIF where you can
Host video on a video platform or behind a streaming setup, not as a file in Documents and Media
For large scripts, check your themes and client extensions for bundled libraries the page does not use
Done when the auditor finds nothing over the limit.
9
Fix the web fontsVerifies with Web FontFonts block text. Until the font file arrives, the browser either shows nothing or shows a fallback and then shifts the layout. Themes often carry more font files than the design uses.
The Web Font Auditor reads the stylesheets and checks every
@font-face. It fails fonts withoutfont-display, withfont-display: blockorauto, or without a WOFF2 file. It warns about fonts from another host, more than two families, files over 100 KB and missing fallback metrics.Do this
In the theme's CSS, add
font-display: swap(oroptional) to every@font-faceShip WOFF2. Remove the older formats
Serve the fonts from your own host or CDN instead of a third-party font service
Keep to two families, and prefer one variable font over many static weights
Done when the auditor passes. Warnings keep this step from completing by itself. Mark it complete if the ones that remain are choices you made.
10
Tune the JVM and application serverFrom here on, nothing can be checked from outside. These are the starting values from Liferay's go-live checklist. They were tested on Liferay's reference hardware, so confirm them under your own load.
Do this
Set
-Xmsand-Xmxto the same value so the JVM never resizes the heap. Do not go above 32 GBUse JDK 21 on current releases. Choose G1 or Shenandoah as the collector and turn on GC logging, so you can see pause times
Start the Tomcat thread pool at 50 threads. A pool that is too small shows slow responses with idle CPU. One that is too large shows very high CPU
Size the database connection pool at 30 to 40 percent of the thread pool, and watch for threads waiting on a connection
Stop the operating system from swapping the JVM's memory to disk
Run nothing else on the Liferay server. Elasticsearch in particular competes for the same resources
On virtual machines, check that CPU and memory are reserved for the server and not shared out to more machines than the host has
Done when the settings are applied and a load test shows stable response times with CPU below about 75 percent.
11
Tune LiferayLiferay ships with most settings ready for production. These are the ones its go-live checklist says to review.
Do this
Set
counter.increment=2000. Liferay reserves IDs from the database in blocks, and the default block is small for a busy siteTurn off servlet filters you do not use, such as
com.liferay.portal.sharepoint.SharepointFilter=falseReduce
locales.enabledto the languages the site usesKeep
dl.file.entry.preview.fork.process.enabled=true, so generating document previews runs in a separate process and cannot stall page requestsIn the Search Results widget, set default display page templates for your content types. Without them Liferay searches for a page to show each result on
Watch cache hits and misses over JMX. A high miss rate after the server has warmed up means a cache is too small. Raising a cache size raises memory use, so change one at a time
If Liferay checks passwords itself, sign-in is CPU-heavy by design. Move authentication to SSO if sign-in volume is high. Do not weaken the hashing
Done when you have reviewed each item and applied the ones that fit your site.
12
Give Elasticsearch its own resourcesSearch sits behind more of Liferay than the search page: content listings, the Control Panel and many widgets query the index. A slow index makes ordinary pages slow.
Do this
Never run the bundled sidecar Elasticsearch in production. It is for development and Elastic does not support it
Run Elasticsearch on its own servers, sized like the Liferay servers: at least eight CPU cores and 16 GB of memory
Give the heap half of the server's memory, never more than 30 GB, through
ES_JAVA_OPTS. The rest is for the operating system's file cache, which Elasticsearch depends onUse SSD storage. Avoid network file systems
Turn off swap and set
bootstrap.memory_lock: trueRaise
vm.max_map_countto 262144 and the open file limit to 65536Stay on an Elasticsearch version listed in Liferay's search engine compatibility matrix for your release
Done when Elasticsearch runs on dedicated servers with these settings.
13
Monitor and load testThe numbers in the last three steps are starting points. The right values for your site come from watching it under load, and that needs tools in place before go-live.
Do this
Install an application performance monitoring tool. DXP 2023.Q4 and later ship with Glowroot. Dynatrace, New Relic and AppDynamics are common alternatives
Watch garbage collection pauses, thread pool use, connection pool use and slow database queries
Write a load test that looks like real traffic: anonymous page views, sign-ins, searches and the pages your custom code serves
Run it at your expected peak, then again with one server removed from each tier
Change one setting at a time and run the test again
Done when the site holds its response times at peak load in the test, and the monitoring that showed you that is running in production.
14
Measure againVerifies with Time To First Byte AuditorThis is the same measurement as the first step. Compare the two numbers.
The Time To First Byte Auditor passes at an average of 800 ms or less.
Do this
Run the auditor
If it is still above 800 ms, open your monitoring tool and find where the time goes on that page. On Liferay the usual causes are a slow database query, a widget or fragment that calls an external service while rendering, a search query, or a cold cache after a restart
Run the auditor from this journey on your heaviest pages as well, not only the home page
Done when the auditor passes.
Next in the series: How to keep your Liferay site online. Cache and CDN, which you set up here, are also what keeps the origin standing in a traffic spike.
Create an account, add your site as a monitored URL and work through these steps on it.