configuration.php and .htaccess
|
This content was generated with the assistance of AI and should be verified against the official Joomla documentation before being relied on in production. This sub-section’s Joomla bibliography lists the reference material consulted while preparing these pages. |
This page was written against Joomla 6.2.x and PHP 8.3+ and checked on 2026-10-30. It describes the two plain files
that decide how a Joomla site starts and how its URLs reach PHP: configuration.php, the PHP class that holds every
setting of the Global Configuration screen plus the database credentials, and htaccess.txt, the Apache rewrite
file that the User Guide tells administrators to rename to .htaccess. The running example is the fictitious Tallpine
Hiking Club site, which lives in a Docker Compose stack locally and on two hosts (staging and production).
Who needs this page: the administrator who rotates a database password or moves the site, the site builder who enables clean URLs, and the developer who deploys the same code to several environments. The screen that edits most of the file is described tab by tab in Global Configuration; this page is about the file itself, the properties the screen does not show, and the web server rules around it. The web server side (virtual hosts, TLS, reverse proxy) is in Web Server, Reverse Proxy and HTTPS, the code tree layout in Installation and the Public Folder.
How the file is used
The installer writes configuration.php in the site root as the last step of the installation. The file contains one
class, JConfig, whose public properties are the settings. During bootstrap, includes/framework.php checks that the
file exists and is larger than ten bytes, includes it, and builds the application configuration from an instance of
that class. If the file is missing and no installation folder exists, Joomla stops with the message that no
configuration file was found and no installation code is available; if the installation folder is still present, the
web installer starts. The location is held in the constant JPATH_CONFIGURATION, which includes/defines.php sets to the
site root. A defines.php file placed next to index.php is included first and can override it, which is how some
hosts keep the file outside the code tree.
Two consequences matter in practice. The file is plain PHP, so a syntax error (a missing semicolon after an edit) takes the whole site down with a blank page or a fatal error before any Joomla error page can be rendered. And because the class is instantiated on every request, anything that is valid PHP in a property default or in a constructor is evaluated every time; that is useful for reading environment variables (see below) and harmful for slow code.
The properties are read through the application, $app->get('name'), never by reading the file again. Extensions
that need a setting use the application object (or the injected configuration), and Factory::getConfig() has been
deprecated for that reason. The same registry backs the CLI commands config:get and config:set.
The anatomy of JConfig
The installer template is installation/configuration.php-dist in the source tree. It groups the properties with
comments; the groups below follow it. Properties in parentheses are the ones a normal installation fills in differently
from the template.
| Group | Properties | Meaning |
|---|---|---|
Site |
|
Offline mode and its message, site name, default editor and captcha plugin, list length, default access level, front-end editing. |
Database |
|
Driver ( |
Server |
|
The site secret, output compression, error level, help URL template, writable folders, canonical URL and HTTPS enforcement. |
Locale and session |
|
Time zone, session lifetime in minutes and the session store with its connection settings. |
|
Outgoing mail; the credentials are secrets (see Mail Templates and Email Settings). |
|
Cache |
|
System cache and its store (see Caching and Performance). |
Logging |
|
What the logger writes into |
CORS |
|
Cross-origin headers for the Web Services API; the template allows every origin when CORS is turned on, which is rarely right for production. |
Proxy and debug |
|
Outgoing HTTP proxy used by Joomla itself (update checks, extension downloads) and the debug switches. |
Meta, SEO and feeds |
|
Default meta data and URL options (see SEO, SEF URLs, Redirects and Schema.org). |
Cookies and miscellaneous |
|
Cookie scope, the root asset id of the ACL tree and the reverse-proxy switch. |
A few properties exist in the file but not on the Global Configuration screen. dbprefix, dbtype, host, user, db
and the dbssl* values can only be changed by editing the file or with config:set; live_site has no field at all; and
asset_id must be left alone because it ties the file to the root row of the permissions tree in the database.
Older installations can contain extra properties that current releases ignore, such as ftp_* values from the days of the
FTP layer or the root_user property that the installer left behind in Joomla 3; the Global Configuration model can
remove root_user on request, so a file with one is not an error.
The settings that matter most
secret
secret is a random string generated by the installer (16 characters of UserHelper::genRandomPassword()). Joomla mixes
it into hashes rather than using it as an encryption key, and several features depend on it:
-
the session name and the hash that identifies the application (
md5($secret . $seed)) -
the cache storage hash
-
the language cookie name of the language filter
-
token-based features: the API token plugin and the user token plugin derive their HMACs from the site secret
-
the WebAuthn credential repository and the Proof-of-Work captcha added in 6.1, which signs its challenges with it
The practical rules are therefore the following. Generate a different value for each environment. Never copy the staging
file to production. Do not commit it; the real file is produced at deployment. After a leak, change it: every logged-in
session ends, existing API tokens issued for users stop validating and have to be regenerated, and cached data becomes
unreadable and is rebuilt. Rotating the secret is a legitimate emergency step, but treat it as a maintenance-window
operation. The secret is among the fields that the Global Configuration model refuses to show in the form, which is why
the screen never discloses it.
Database settings
dbtype is mysqli, pdomysql or pgsql. The old mysql and pgsql extension drivers disappeared in Joomla 4; when
a file still says mysql, the database service provider switches to the PDO MySQL driver, and Microsoft SQL Server has not
been supported since 4.0. host accepts a host name, host:port, or a socket. The Docker examples in this
documentation use the Compose service name (db). dbprefix is a short string (letters, digits and underscores, starting
with a letter, at most 15 characters) that is put in front of every table name. A random prefix only makes the table names
harder to guess; it is not a security control and it must not be changed in the file alone, because the tables in the
database keep their old names.
dbencryption, dbsslverifyservercert, dbsslkey, dbsslcert, dbsslca and dbsslcipher configure TLS between PHP
and the database server. Managed databases usually require dbencryption = 1 and a CA file; the Global Configuration
screen checks the connection before it saves these values, and a wrong value is the most common reason that a site stops
working after an edit.
tmp_path and log_path
tmp_path is where Joomla unpacks extension packages, stores uploads and writes update packages. log_path is where
the log files (joomla_update.php, everything.php, deprecated.php and the files of extensions that register a
logger) are written. The defaults are the tmp folder in the site root and administrator/logs. The template in
configuration.php-dist ships /tmp and /administrator/logs as placeholders, which the installer replaces with
absolute paths.
The Global Configuration form tells administrators to use absolute paths that are writable by PHP and warns against
the system-wide /tmp. The reason is that a shared temporary directory exposes extracted packages and uploads to other
users of the host. The log folder description adds that if the folder is missing or not writable, Joomla will not load at
all. When saving, the model checks both folders; for a custom path that is not writable it falls back to the default
folder and shows a warning. A path that was valid on the old host and does not exist on the new one is the classic
symptom after a move (see below).
// Typical values on the Tallpine production host (absolute paths)
public $tmp_path = '/srv/tallpine/tmp';
public $log_path = '/srv/tallpine/logs';
Keep both folders outside the web root when the public folder layout is used (see Installation and the Public Folder); otherwise protect them with the rules in the web server sections below, because log files can contain user names, IP addresses and stack traces.
live_site
live_site is an optional full URL such as https://tallpine.example. When it is empty, which is the normal case, Joomla
derives the base URL from the request: the scheme from HTTPS or HTTP_X_FORWARDED_PROTO, the host from HTTP_HOST and
the path from the script name. When it is set, Uri::base() uses it instead and upgrades http:// to https:// when the
request itself is secure. The command-line application also reads it, and falls back to a placeholder
(https://joomla.invalid/set/by/console/application) if it is empty, which is why links generated by scheduled tasks and
console commands can be wrong until it is set. Pitfalls:
-
A wrong
live_sitebreaks assets, redirects and the administrator login in every environment that does not use that exact URL, so a file copied from production to staging withlive_sitestill set sends staging users to production. -
The value must not end with a slash and must not include
/administrator. -
Leave it empty for a site reachable under several host names (multi-domain, multilingual by domain).
-
Set it, or let the environment set it, only when the application is behind something that rewrites the host or path and the headers cannot be corrected, or when a command-line job must generate absolute URLs.
force_ssl
force_ssl takes 0 (none), 1 (administrator only) or 2 (entire site). The installer writes 0. The effect is spread
over several places:
-
the administrator application redirects a non-HTTPS request with a 301 when the value is 1 or higher
-
the site router attaches a rule that redirects non-HTTPS requests when the value is 2
-
the session and cookie code marks cookies as secure when HTTPS is forced for the client (
isHttpsForced()is true for the site client at 2 and for the administrator client at 1 or more)
Before the screen saves a non-zero value it makes an HTTPS request to the site’s own host (ignoring certificate errors)
and resets the value to 0 with a warning if the request does not succeed with an expected status. Editing the file
bypasses that check, so enabling force_ssl in the file on a server without a certificate locks everybody out. The reverse
also applies: behind a TLS-terminating proxy that talks plain HTTP to PHP, enabling it without the next setting produces a
redirect loop.
behind_loadbalancer
behind_loadbalancer (the Behind Load Balancer switch) tells Joomla that the connection comes from a trusted proxy.
includes/framework.php, administrator/includes/framework.php and api/includes/framework.php read it very early and
call IpHelper::setAllowIpOverrides(true) when it equals 1, and setAllowIpOverrides(false) otherwise. With it on, headers
such as X-Forwarded-For may replace REMOTE_ADDR, so that the user’s real address appears in the action log, in
brute-force protections and in the Privacy tools. With it off (the default), those headers are ignored. The framework
file explains the reason: the library default is to allow overrides, which is wrong for a site that is not behind a trusted
proxy, because any visitor could then forge their address.
Therefore turn it on only when every request really passes through a proxy that sets the headers itself and strips them
from incoming requests. The post-installation message "Behind Load Balancer" shows up when Joomla sees an
X-Forwarded-For or Client-IP header while the setting is off. The setting concerns client addresses; the scheme is read
separately from HTTP_X_FORWARDED_PROTO when the URI is built, so the proxy must send that header as well. The matching
proxy configuration is in Web Server, Reverse Proxy and HTTPS.
A configuration.php for several environments
A file that is committed or baked into an image cannot contain real credentials. PHP allows an expression in a
constructor, so the JConfig class can read the environment. The following file is the deployment template for the
Tallpine site; nothing in it is a real secret, and the values come from variables set by Docker Compose, Kubernetes or
the host’s service manager.
<?php
/**
* Deployment template. Do not edit through Global Configuration
* (see the pitfall about saving below).
*/
class JConfig
{
public $offline = false;
public $sitename = 'Tallpine Hiking Club';
public $editor = 'tinymce';
public $list_limit = 20;
public $access = 1;
public $dbtype = 'mysqli';
public $host = 'db';
public $user = 'tallpine';
public $password = '';
public $db = 'tallpine';
public $dbprefix = 'tp_';
public $dbencryption = 0;
public $secret = '';
public $gzip = false;
public $error_reporting = 'none';
public $tmp_path = '/srv/tallpine/tmp';
public $log_path = '/srv/tallpine/logs';
public $live_site = '';
public $force_ssl = 0;
public $behind_loadbalancer = false;
public $offset = 'UTC';
public $lifetime = 15;
public $session_handler = 'database';
public $caching = 1;
public $cache_handler = 'file';
public $cachetime = 15;
public $mailer = 'smtp';
public $smtphost = 'mailpit';
public $smtpport = 1025;
public $smtpauth = false;
public $smtpuser = '';
public $smtppass = '';
public $smtpsecure = 'none';
public $debug = false;
public $sef = true;
public $sef_rewrite = true;
public $asset_id = 1;
public function __construct()
{
$this->host = getenv('TALLPINE_DB_HOST') ?: $this->host;
$this->password = getenv('TALLPINE_DB_PASSWORD') ?: 'CHANGE_ME';
$this->secret = getenv('TALLPINE_SECRET') ?: 'CHANGE_ME_TO_A_LONG_RANDOM_VALUE';
$this->smtppass = getenv('TALLPINE_SMTP_PASSWORD') ?: '';
$this->live_site = getenv('TALLPINE_LIVE_SITE') ?: '';
$this->force_ssl = (int) (getenv('TALLPINE_FORCE_SSL') ?: 0);
$this->behind_loadbalancer = getenv('TALLPINE_BEHIND_PROXY') === '1';
}
}
Compose passes the variables (a .env file kept out of version control, or a secrets mechanism, supplies the values):
services:
joomla:
image: joomla:6.2-php8.3-apache
environment:
TALLPINE_DB_HOST: db
TALLPINE_DB_PASSWORD: ${TALLPINE_DB_PASSWORD:?set it in .env}
TALLPINE_SECRET: ${TALLPINE_SECRET:?set it in .env}
TALLPINE_BEHIND_PROXY: "1"
TALLPINE_FORCE_SSL: "2"
volumes:
- ./deploy/configuration.php:/var/www/html/configuration.php:ro
Source: Docker Official Image: joomla
Mounting the file read-only is the strongest way to keep Global Configuration from rewriting it, and the screen’s notice
that configuration.php is not writable is then expected rather than a fault. The official image itself reads
JOOMLA_DB_HOST, JOOMLA_DB_USER, JOOMLA_DB_PASSWORD (or JOOMLA_DB_PASSWORD_FILE), JOOMLA_DB_NAME and
JOOMLA_DB_TYPE during its first start, and writes a normal file; the image variables only seed the installation, and
changing them afterwards does nothing. Container operations are covered in
Compose for local development and
Local Development with Docker; on Kubernetes, the
database password and the secret belong in a Secret object, described in
Kubernetes Secrets. When sessions or cache move to Redis, the session_redis_*
and redis_* properties hold the host, port and AUTH value (a secret too); see
Redis Reference for the server side.
The pitfall that the comment in the template mentions is how Global Configuration saves. The model builds the new file
from the submitted form data, and for the protected fields (password, secret, smtppass, the OAuth2 mail values and
the Redis AUTH values) it takes the current values from the running application. When an administrator presses Save,
the values that the constructor read from the environment are written into the file as literals. On a deployment that is
meant to be environment-driven, mount the file read-only or never save the screen on that host.
Editing safely and permissions
Joomla keeps the file read-only. When Global Configuration saves, it switches the file to mode 0644 if the PHP user owns
it, rewrites it as a PHP class named JConfig, and sets it back to 0444. The rewritten file is regenerated from the
submitted values, which has two consequences: comments and custom code in the file are lost, and properties that are not
known to the form are kept only if they were loaded. Hand-written constructors, as in the previous section, do not survive
a save.
The recommended procedure for a manual edit is:
-
Make a copy:
cp configuration.php configuration.php.bak, outside the web root if possible (a.bakfile below the web root is served as plain text on a server that does not run PHP for that extension). -
Make the file writable for the edit, change it, and run
php -l configuration.phpto catch syntax errors. -
Restore the mode and the owner.
-
Clear the cache and reload the site.
cd /srv/tallpine/public_code
sudo -u deploy cp configuration.php /srv/tallpine/backup/configuration.php.$(date +%F)
sudo -u deploy chmod 0644 configuration.php
sudoedit configuration.php
php -l configuration.php
sudo -u deploy chmod 0444 configuration.php
The command line avoids the editor altogether and validates what it writes:
php cli/joomla.php config:get sef sef_rewrite force_ssl behind_loadbalancer
php cli/joomla.php config:set sef_rewrite=true force_ssl=2 behind_loadbalancer=true
php cli/joomla.php config:set tmp_path=/srv/tallpine/tmp log_path=/srv/tallpine/logs
Source: Joomla User Guide: Using the CLI
config:set takes option=value pairs, checks the database connection when database options change, validates the
prefix against the same rules as the installer, and calls the same model as the screen, so it also runs the force_ssl
check, the path checks and the protected-field handling. More commands are in
Command Line Interface.
The permission model is as follows.
| Item | Recommended mode | Reason |
|---|---|---|
|
0444 (0440 when the web server group differs from the owner) or 0400 |
The file holds the database password and the secret; PHP needs to read it, rarely to write it. |
|
0755 owned by the PHP user |
These are the folders that PHP must write to. |
Everything else in the code tree |
0755 for folders and 0644 for files, owned by a deploy user and not writable by PHP |
Limits what an exploited extension can overwrite; the cost is that the web updater and the extension installer cannot work, so update through the deployment pipeline or run the CLI as the deploy user. |
Never keep configuration.php in version control with real values. Keep a template in the repository (as above) and keep
the real file in the secret store. The repository’s secret scanner will otherwise flag the file, and a leaked file means a
leaked database password and secret. Hosting panels often show the file in a file manager with 0644; check the mode after
every restore, because a restored backup rewrites it. Joomla’s Security Centre and the hardening checklist in
Security Hardening, MFA and Passkeys list the same measures.
Errors caused by the file
| Symptom | Likely cause |
|---|---|
Blank page after an edit |
PHP syntax error; run |
The installer appears again |
|
"Error displaying the error page" or database connection error |
Wrong |
"Joomla will not load" with a log folder message |
|
Extension installation fails with a temp folder error |
|
Redirect loop on every page |
|
Wrong client IP in logs, or all users share one address |
|
Everybody is logged out after a deployment |
|
Settings revert after saving |
The file is mounted read-only or owned by another user; the save notice says it could not be written. |
htaccess.txt
Joomla ships htaccess.txt in the site root. Apache only reads a file called .htaccess (the name is set by the
AccessFileName directive), so the file is activated by copying or renaming it. If the host already provides an
.htaccess with its own rules, merge the two rather than replacing it. The User Guide recommends editing .htaccess
rather than htaccess.txt, because updates can overwrite the latter, and after a Joomla update it is worth comparing
your .htaccess with the current htaccess.txt, since the file changes between versions (the SEF section, for example,
gained the API application rules in Joomla 4). Older books and tutorials show the Joomla 3 or 4 contents of this file;
Developing Extensions for Joomla! 5 (Packt, 2023) and Joomla! 4 Masterclass (Packt, 2023) predate the current layout
and do not describe the API rewrite or the pre-compressed asset section.
cd /var/www/tallpine
cp htaccess.txt .htaccess
php cli/joomla.php config:set sef_rewrite=true
The 6.2 file has these sections, in order.
| Section | What it does |
|---|---|
Options |
|
Security headers |
|
Common exploits |
Four |
Custom redirects |
An empty area for your own 301 rules, such as the canonical |
RewriteBase |
A commented-out |
Core SEF section |
First, a rule that copies the |
Fallback without mod_rewrite |
When |
GZIP and Brotli |
Serves a pre-compressed |
Source: Joomla CMS source: htaccess.txt
The file is only half of the story. The Use URL Rewriting option of Global Configuration (the sef_rewrite property)
removes index.php from generated URLs; the rewrite rules make those URLs work. Turning the option on without the file,
or on a server where AllowOverride is None, makes every page except the home page return 404. The related checklist
(FollowSymlinks, AllowOverride All, the module list) is in SEO, SEF URLs, Redirects and Schema.org.
OpenLiteSpeed reads .htaccess but caches it: restart the server after a change.
Security reasoning: Apache’s .htaccess is convenient but costs a file-system lookup per request and lets every
writable directory carry its own rules. If you control the server, place the same directives in the virtual host and set
AllowOverride None; the rules above translate one to one.
<VirtualHost *:443>
ServerName tallpine.example
DocumentRoot /var/www/tallpine/public
<Directory /var/www/tallpine/public>
AllowOverride None
Require all granted
Options -Indexes +FollowSymLinks
RewriteEngine On
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
RewriteCond %{REQUEST_URI} ^/api/
RewriteCond %{REQUEST_URI} !^/api/index\.php
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule .* api/index.php [L]
RewriteCond %{REQUEST_URI} !^/index\.php
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule .* index.php [L]
</Directory>
Header always set X-Content-Type-Options "nosniff"
# SSLEngine and certificate directives omitted
</VirtualHost>
Source: Joomla CMS source: htaccess.txt
nginx equivalent
nginx ignores .htaccess. The User Guide pages on SEF URLs with nginx and on nginx hosting notes give the short form: a
try_files fallback to index.php, PHP handed to PHP-FPM, a long expiry for static files and a deny rule for scripts in
writable folders. On Joomla 4.0 and later the API application needs its own location, which older nginx notes do not
mention and which is the usual reason that the Web Services API and the headless front end fail with 404 on nginx.
server {
listen 443 ssl;
server_name tallpine.example;
root /var/www/tallpine/public;
index index.php;
add_header X-Content-Type-Options "nosniff" always;
# Exploit filter equivalent to the htaccess.txt query-string rules
if ($query_string ~* "(base64_encode[^(]*\([^)]*\)|GLOBALS(=|\[|%[0-9A-Z]{0,2})|_REQUEST(=|\[|%[0-9A-Z]{0,2}))") {
return 403;
}
# SVG files must not run script when opened directly
location ~* \.svg$ {
add_header Content-Security-Policy "script-src 'none'" always;
add_header X-Content-Type-Options "nosniff" always;
try_files $uri =404;
}
location /api/ {
try_files $uri $uri/ /api/index.php?$args;
}
location / {
try_files $uri $uri/ /index.php?$args;
}
# No PHP execution in writable folders
location ~* ^/(images|media|cache|tmp|logs)/.*\.(php|phtml|phar)$ {
return 403;
}
location ~ \.php$ {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param HTTP_AUTHORIZATION $http_authorization;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
}
location ~ /\.ht { deny all; }
}
Points to check: with the public folder root points to the public directory and configuration.php is not reachable at
all; without it, add location = /configuration.php { deny all; } and deny the libraries, cache and tmp directories
too. Test the exploit filter with a request such as ?x=GLOBALS[a]=1, which must return 403. The HTTP_AUTHORIZATION line forwards the
Authorization header to PHP-FPM, which the API token authentication needs. The nginx notes page in the User Guide is
older than Joomla 4; its sample targets PHP 5 era sockets and a return 403 followed by an error_page line that never
runs, so adapt it rather than copying it.
IIS equivalent
Microsoft IIS reads web.config. Joomla ships web.config.txt, which is renamed to web.config and requires the URL
Rewrite Module. It contains three rules with the same logic as htaccess.txt: Joomla! Common Exploits Prevention
(four query-string conditions, returns 403 and stops processing), Joomla! API Application SEF URLs (rewrites to
api/index.php) and Joomla! Public Frontend SEF URLs (rewrites to index.php), together with
directoryBrowse enabled="false" and an X-Content-Type-Options: nosniff header; the cross-origin headers are
commented out as in the Apache file. It has no counterpart for the SVG header or the pre-compressed assets: if you need
them, add a customHeaders entry and enable IIS static compression. IIS is a supported platform (the Developer Manual
lists IIS 10), unlike Drupal where IIS support was removed in 11.0.
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<location path=".">
<system.webServer>
<directoryBrowse enabled="false" />
<rewrite>
<rules>
<!-- The "Common Exploits Prevention" rule (403 on four query-string patterns) comes first in the shipped file -->
<rule name="Joomla! API Application SEF URLs">
<match url="^api/(.*)" ignoreCase="false" />
<conditions logicalGrouping="MatchAll">
<add input="{URL}" pattern="^/api/index.php" ignoreCase="true" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsFile" ignoreCase="false" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" ignoreCase="false" negate="true" />
</conditions>
<action type="Rewrite" url="api/index.php" />
</rule>
<rule name="Joomla! Public Frontend SEF URLs">
<match url="(.*)" ignoreCase="false" />
<conditions logicalGrouping="MatchAll">
<add input="{URL}" pattern="^/index.php" ignoreCase="true" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsFile" ignoreCase="false" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" ignoreCase="false" negate="true" />
</conditions>
<action type="Rewrite" url="index.php" />
</rule>
</rules>
</rewrite>
<httpProtocol>
<customHeaders>
<add name="X-Content-Type-Options" value="nosniff" />
</customHeaders>
</httpProtocol>
</system.webServer>
</location>
</configuration>
The block above is the shipped file without the exploit-prevention rule and the commented headers. Hide the writable folders
with the IIS request filtering feature and the configuration.php file with a hiddenSegments entry if the code tree is
under the site root.
Moving a site between hosts
The User Guide documents moving a site between subdirectories and gives general migration advice (back up first, test on a copy, check extension compatibility); the host-to-host steps below follow the same logic and the file format described above. Use a backup tool or a manual copy; Joomla has no built-in backup (see Backups and Recovery).
-
Prepare the target. Check PHP 8.3+, extensions and database against Requirements and Hosting. Joomla does not support downgrades or moving between MySQL and PostgreSQL, so restore into the same database family and a version that is not older.
-
Put the site offline (Global Configuration, or
config:set offline=true) and take a final backup of files and database. -
Copy the files and the database dump. Exclude
cache,administrator/cache,tmpand the logs; keepimages,media, templates andconfiguration.php. -
Create the database and user on the new host and import the dump. Keep the table prefix.
-
Edit
configuration.php:host,user,password,db,dbtype,dbencryption,tmp_path,log_path, andlive_site(remove or correct it). Generate a newsecretif the old environment is not trusted anymore, and accept that sessions and API tokens reset. Checksession_*,cache_*andredis_*values,cookie_domain,force_sslandbehind_loadbalancer. -
Edit the web server rules: copy
htaccess.txtto.htaccess(or the nginx or IIS equivalent), comment out or correctRewriteBasefor a subdirectory, and make sure the document root is the public folder if you use it. -
Fix permissions and ownership for the new PHP user, and create the tmp and log folders.
-
Clear the cache in the administrator (System, Clear Cache), then reload the site and test the home page, a deep SEF link, the administrator login, a media upload, an extension install, mail sending (see Mail Templates and Email Settings) and the Web Services API.
-
Switch DNS, put the site online and watch the logs. Keep the old host untouched until the scheduled tasks and the update checks have run once on the new one (see Scheduled Tasks).
# On the old host
php cli/joomla.php config:set offline=true
mysqldump --single-transaction --no-tablespaces tallpine > tallpine.sql
tar --exclude=cache --exclude=tmp --exclude=administrator/cache --exclude=administrator/logs \
-czf tallpine-files.tar.gz -C /var/www/tallpine .
# On the new host (credentials come from the environment)
mysql -h "$TALLPINE_DB_HOST" -u tallpine -p tallpine < tallpine.sql
tar -xzf tallpine-files.tar.gz -C /var/www/tallpine
php /var/www/tallpine/cli/joomla.php config:set host="$TALLPINE_DB_HOST" tmp_path=/srv/tallpine/tmp log_path=/srv/tallpine/logs
php /var/www/tallpine/cli/joomla.php config:set offline=false
# live_site is not a Global Configuration field: remove or correct it in configuration.php by hand
The same list explains the two common failures after a move. A site that shows the installer was copied without
configuration.php (or with an empty one). A site that loads but shows no styling or sends users to the old domain still
has live_site set, or its .htaccess carries an old redirect. Moving to a subdirectory or from one removes the
subdirectory from log_path, tmp_path and live_site and from the RewriteBase line; the User Guide page lists
$ftp_root too, which only exists in files created by old releases. Moving between environments that share a code
base (staging to production) is better done with deployments plus a separate database restore than by copying the
configuration.
On a cloud deployment, the same file is the reason why more than one web node needs shared state: with more than one
machine, session_handler, the system cache and the tmp and images folders have to be shared or externalised, and
the database must be a separate service. That topic is in
Multi-site Options and Scaling.
Version notes and corrections
-
The
JConfigformat has been stable since Joomla 4: the template of 6.2 adds no property that a 4.x file lacks, apart from values introduced by individual features (the CORS block, Redis session settings andbehind_loadbalancer). -
JPATH_PLATFORMis no longer defined in Joomla 6 except through the Behaviour - Backward Compatibility 6 plugin; custom files that includeconfiguration.phpshould guard withdefined('_JEXEC') or die;instead. -
htaccess.txtcarries the API rewrite rules needed since the Web Services API arrived in Joomla 4, an SVG content-security rule, pre-compressed asset serving and commented cross-origin isolation headers; compare your.htaccessafter major updates. -
Developing Extensions for Joomla! 5 and Joomla! 4 Masterclass describe a layout that still works, but neither covers the public folder (5.0), TUF-verified updates (5.1), Automated Core Updates (5.4/6.0) or the Joomla 6 defaults. With Automated Core Updates, a read-only
configuration.phpis not a problem, but a read-only code tree is: the updater must be able to write the files, so deployments that lock the tree must disable the feature and update through their pipeline (see Updating Joomla and Automated Core Updates). -
The legacy wiki pages on
configuration.phpand on.htaccessare marked obsolete; the User Guide and the source tree are the current references. See also Where Older Books and Tutorials Are Outdated.