How to secure your Liferay site
Patch what is vulnerable, close the endpoints Liferay exposes by default, and harden TLS, cookies and headers. Part one of the Liferay series.
A Liferay site that was installed with default settings answers anonymous requests on its API pages, tells every visitor which product it runs, and leaves most browser protections to you. None of this is hard to fix, but it is spread across portal-ext.properties, Tomcat and the web server in front of them, so it rarely gets done in one sitting.
This journey puts it in order. First you tell 37 Audits which Liferay version you run, so the checks know what to look for. Then you patch, close what is exposed, and work outward through HTTPS, cookies and browser headers. The last two steps cover what cannot be seen from outside: sign-in settings and the servers behind Liferay.
Most steps have an auditor that checks the result on your monitored URL. A few complete by hand, either because nothing outside your network can verify them or because a correct Liferay setup still leaves a finding you have to judge yourself. Each of those steps says so.
This is the first of three Liferay journeys. The others are How to make your Liferay site fast and How to keep your Liferay site online.
Steps
Open a step to read it1
Register your Liferay versionThree auditors in this journey read the software stack of your project, not the site. Without a Liferay entry the CVE and Latest Version auditors have nothing to look up and the Protected URL Auditor probes generic paths like
/admininstead of the Liferay ones.Do this
Open the project that owns this URL and find Software Stack
Choose Add Software and set the vendor to
LiferayPick the product (
DXPorPortal) and the version from the list the form offers. It is the list of releases Liferay publishes, so an installed release is always in it:2025.Q1.16 LTS,7.4 U101,7.4.3.132 GA132Find the version under Control Panel → Server Administration, or in the startup log
Done when the project lists Liferay with the version that is running in production. Update it after every upgrade, or the next steps will check the wrong release.
2
Patch known vulnerabilitiesVerifies with CVE AuditorLiferay publishes every security advisory with the versions it affects. Attackers read the same list, and a public advisory against the version you run is the easiest way into the site.
The CVE Auditor compares the version you registered against Liferay's known vulnerabilities. It fails on any advisory scored 7.0 or higher and warns on lower ones.
Do this
Run the auditor and open the findings
For each advisory, read which update, fix pack or quarterly release fixes it
Plan the update. If you cannot update now, apply the workaround the advisory describes
After updating, change the version in the Software Stack and run again
Done when the auditor finds no advisory that affects your version. If the fix for an advisory exists only on a newer line, your line may be out of support. How to keep your Liferay site online checks that with the End of Life auditor.
3
Apply the latest fix release of your lineVerifies with Latest Version AuditorLiferay ships fixes for a release line as new releases of that line: updates and fix packs on 7.x, patch releases on the quarterly lines (
2025.Q1.16after2025.Q1.15). That is where security patches land, and an advisory often says no more than "fixed in the latest release of your line".The Latest Version auditor compares the version in your 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.
Do this
Run the auditor. A failure names the fix release you should be on
Apply it. A fix release stays inside your line, so the update is small and your customisations usually need no change
Decide how often you take fix releases, and put it in the calendar. Liferay publishes them through the year
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 a different decision, covered in How to keep your Liferay site online. That warning stays while you are on an LTS line, so this step completes by hand.
4
Close the endpoints Liferay exposesVerifies with Protected URL AuditorA default Liferay answers anonymous visitors on pages that list every remote service it offers.
/api/jsonwsdocuments the JSON web services,/o/apiis the headless API explorer, and/c/portal/json_serviceis the older JSON servlet. They are meant for developers and they give an attacker a map.The Protected URL Auditor requests these paths and the Control Panel without signing in. A path is protected when it answers 401, 403 or 404, or redirects to the sign-in page.
Do this
At the web server or load balancer, deny outside requests to
/api/jsonws,/o/apiand/c/portal/json_service, or allow only your office and integration addressesIn
portal-ext.properties, setjsonws.web.service.api.discoverable=falseso the JSON web services page stops listing servicesRestrict the servlets by address with
jsonws.servlet.hosts.allowedandjson.servlet.hosts.allowedCheck that the Control Panel sends anonymous visitors to sign-in
Add paths of your own, such as a custom servlet, in the auditor's
pathspreference on the URL's Configuration tab
Done when the auditor reports every path as protected. Test your integrations afterwards: anything that calls these APIs from outside needs to be on the allow list.
5
Stop advertising server versionsVerifies with Server Fingerprint AuditorKnowing the exact version of Tomcat, Apache or Liferay lets an attacker pick the right exploit without probing. These versions travel in response headers that nobody needs.
The Server Fingerprint Auditor reads
Server,X-Powered-ByandX-AspNet-Versionand fails when one carries a version number. It does not read Liferay's ownLiferay-Portalheader, so check that one yourself.Do this
Apache:
ServerTokens ProdandServerSignature Off. Nginx:server_tokens offTomcat: keep
xpoweredByoff on the connector and setshowServerInfo="false"on theErrorReportValveso error pages stop printing the versionLiferay: set
http.header.version.verbosity=offinportal-ext.propertiesto remove theLiferay-Portalheader
Done when the auditor passes and
curl -sI https://your-site/shows noLiferay-Portalheader.6
Redirect HTTP to HTTPSVerifies with HTTP Redirect AuditorAnyone who types your address without
https://starts on plain HTTP. Until the redirect happens, the request can be read or changed on the way, including the session cookie if it is not marked secure.The HTTP Redirect Auditor requests the
http://version of your URL and expects a permanent redirect tohttps://. A 302 is a warning.Do this
At the web server or load balancer, answer every request on port 80 with a 301 to the same path on HTTPS
In
portal-ext.properties, setweb.server.protocol=httpsso the links Liferay generates use HTTPSIf TLS ends at a proxy, set
web.server.https.portto the public port so Liferay does not build URLs with Tomcat's port
Done when the auditor reports a 301, 307 or 308 to HTTPS.
7
Serve a valid certificate on modern TLSVerifies with Certificate AuditorVerifies with TLS Version AuditorIn most Liferay installations TLS ends at the web server or load balancer, not at Tomcat, so that is where these two checks are fixed.
The Certificate Auditor validates the chain the way a browser does: expired, revoked, self-signed, wrong hostname, untrusted root or missing intermediate all fail. The TLS Version Auditor tries each protocol version and fails when TLS 1.0 or 1.1 is still accepted.
Do this
Install the full chain, intermediates included. A missing intermediate works in some browsers and fails in API clients
Check that the certificate names every hostname the site answers on, including virtual hosts of other Liferay sites
Turn off TLS 1.0 and 1.1
Turn on TLS 1.3. With 1.2 alone the auditor warns and this step will not complete by itself
Done when both auditors pass.
8
Turn on HSTSVerifies with HSTS Preload AuditorThe redirect from step 6 protects a visitor only after the first plain request. HSTS tells the browser to skip that request and use HTTPS from the start, for as long as you say.
The HSTS Preload Auditor expects the header in the form browsers accept for their preload list: a
max-ageof at least one year,includeSubDomainsandpreload.Do this
First confirm that every subdomain of your domain serves HTTPS.
includeSubDomainsapplies to all of them, including intranet hosts and old staging serversAt the web server, on HTTPS responses, send
Strict-Transport-Security: max-age=31536000; includeSubDomains; preloadIf you are unsure, start with a short
max-ageand withoutpreload, and raise it once nothing breaks
Done when the auditor passes. Browsers remember this header for the whole
max-age, so removing it later does not undo it quickly.9
Remove mixed contentVerifies with Mixed Content AuditorA page served over HTTPS that loads an image or script over
http://is only partly protected. Browsers block the scripts and warn about the rest.On Liferay the usual sources are content, not code: web content articles with pasted
http://image links, fragments and widget templates with hardcoded URLs, old theme files, and embedded iframes.The Mixed Content Auditor loads the page in a browser and fails for every request or element that uses
http://.Do this
Run the auditor and open the findings to see each URL
Fix the URL where it lives: the article, the fragment, the template or the theme
Use relative or
https://URLs in new contentweb.server.protocol=httpsfrom step 6 fixes the URLs Liferay generates itself
Done when the auditor finds no
http://resource. Run it on your busiest pages too, not only the home page.10
Secure the session cookiesVerifies with Cookie AuditorJSESSIONIDis the signed-in user. If a script can read it, or it travels over plain HTTP, the session can be stolen.The Cookie Auditor checks every cookie set by the monitored URL. A cookie without
HttpOnlyfails. A missingSecureorSameSiteis a warning.Do this
In Tomcat's
web.xml, inside<session-config>, add a<cookie-config>with<http-only>true</http-only>and<secure>true</secure>If TLS ends at a proxy, make Tomcat aware of it (
secure="true"andscheme="https"on the connector, or theRemoteIpValve), or it will not mark cookies secureAdd
SameSite=Laxthrough Tomcat's cookie processor or at the proxyReview the other cookies in the findings. Liferay sets some of its own, such as
GUEST_LANGUAGE_IDandCOOKIE_SUPPORT, that carry no session data
Done when
JSESSIONIDhasSecure,HttpOnlyandSameSite, and every remaining finding is a cookie you looked at and decided to accept. This step does not complete by itself, because a correctly configured Liferay can still leave such a finding. Mark it complete when you are satisfied.11
Send the browser security headersVerifies with X-Frame-Options AuditorVerifies with X-Content-Type-Options AuditorVerifies with Referrer Policy AuditorVerifies with Permissions Policy AuditorFour response headers switch on protections that browsers leave off by default. They are attached together here because you set them in the same place.
Do this
Check what the site already sends with
curl -sI https://your-site/. Liferay adds some of these itself, depending on the version, and sending a header twice causes its own problemsX-Frame-Options: SAMEORIGINstops other sites from framing yours. If another of your sites embeds Liferay pages in an iframe, that will stop workingX-Content-Type-Options: nosniffstops browsers from guessing file types, which matters for uploaded documentsReferrer-Policy: strict-origin-when-cross-originkeeps full page URLs from leaking to other sitesPermissions-Policy: camera=(), microphone=(), geolocation=(), payment=()turns off device features the site does not use. Allow only the ones a page needsAdd the missing ones at the web server so they also cover error pages and static files
Done when all four auditors pass.
12
Add a Content Security PolicyVerifies with Content Security Policy AuditorA Content Security Policy tells the browser which sources of script, style and frames the page may use. It is the strongest defence against injected script, and the hardest header to get right on a portal, because Liferay pages contain inline scripts.
The CSP Auditor fails when the header is missing and warns when the policy is report-only or allows
unsafe-inlineorunsafe-eval.Do this
Start with
Content-Security-Policy-Report-Onlyand a report endpoint, and watch what real pages would breakList the origins you depend on: CDN, analytics, tag manager, maps, video
Recent DXP releases have a Content Security Policy setting that adds a nonce to Liferay's own inline scripts. Use it if your version has it. On older versions, set the header at the web server
Switch to the enforcing header once the reports are quiet
Done when the enforcing header is live and the pages you care about still work. Many Liferay sites cannot drop
unsafe-inline, and the auditor keeps warning about it, so this step completes by hand. A policy withunsafe-inlinestill limits where scripts and frames can come from, which is better than no policy.13
Harden sign-inNo auditor can see these settings from outside. They are the installation defaults that should not survive into production.
Do this
Change the default administrator. A fresh install creates
test@liferay.comwith the passwordtest(default.admin.*in the portal properties). Rename it and set a strong password, or replace it with named accountsSet
setup.wizard.enabled=falseIf visitors should not create their own accounts, set
company.security.strangers=false, or turn it off under Instance Settings → User AuthenticationLeave
passwords.encryption.algorithmon the PBKDF2 default. Do not lower the rounds to save CPU. If sign-in load is a problem, move authentication to SSOSet a session timeout that fits the data behind the login. Liferay's checklist uses 10 minutes in
web.xml, which overridessession.timeoutRequire a second factor for administrators, in Liferay or in your identity provider
Done when the default account is gone, the setup wizard is off and self-registration matches what the site needs.
14
Lock down what sits behind LiferayLiferay's reference architecture puts a firewall in front of the load balancer and treats everything behind it as trusted. That only holds if nothing behind it answers the internet.
Do this
Elasticsearch: by default it needs no login to search or browse the index, and the index holds your content and user data. Turn on its security features (authentication and encryption) and configure them in Liferay's Elasticsearch connector
Confirm that the Elasticsearch, database and Tomcat ports are not reachable from outside. Test from a machine off your network
JMX: the monitoring flags in Liferay's go-live checklist disable authentication and SSL. That is fine on a closed network and dangerous anywhere else. Bind JMX to an internal interface or require authentication
If your policy calls for it, add firewalls and encryption between tiers
Done when a port scan from outside shows only 80 and 443.
Next in the series: How to make your Liferay site fast.
Create an account, add your site as a monitored URL and work through these steps on it.