Skip to content
Happy Programming Guide
Start learning
PHP

PHP Not Working With Apache on Ubuntu or Debian After an Upgrade

Apache serving PHP files as downloads or blank pages after a release upgrade? The PHP module version no longer matches. Here is the diagnosis and the fix.

A code editor open on a laptop screen

After upgrading Ubuntu or Debian, PHP pages either download as files, show the raw source, or come back blank. Apache is still running; what broke is the link between Apache and PHP. The upgrade installed a newer PHP but left Apache pointing at the old module, or the module was removed and nothing replaced it. The fix is a few commands.

What the upgrade did#

Each Ubuntu and Debian release ships one PHP version. Ubuntu 22.04 has PHP 8.1, 24.04 has 8.3, Debian 12 has 8.2. When you upgrade the distribution, the package manager installs the new PHP but the Apache module for the old one — libapache2-mod-php8.1, say — either disappears or stays enabled while pointing at files that no longer exist.

Apache then has no working PHP handler. It does what it does with any unknown file: serves it as-is.

Which symptom you have#

Symptom Meaning
Browser downloads the .php file No PHP handler loaded at all
Raw PHP source shown in the browser Same, with a text content type
Blank white page PHP runs but hits a fatal error, and errors are hidden
Apache fails to start Config references a module file that is missing

The first two are this article. The third is a different problem — usually a missing extension after the upgrade — and the last is covered below.

Step 1: see what is installed#

Terminal
php -v                              # the command-line PHP
ls /etc/apache2/mods-available/ | grep php
ls /etc/apache2/mods-enabled/ | grep php
apache2ctl -M 2>/dev/null | grep php

Typical broken output: php -v says 8.3, mods-available lists php8.1.load, and the last command prints nothing. Apache knows about a module you no longer have.

Step 2: install the module for the current version#

Terminal
sudo apt update
sudo apt install libapache2-mod-php8.3      # match the version php -v printed

The package name has the version in it. Use whichever php -v reported.

Step 3: disable the old module, enable the new one#

Terminal
sudo a2dismod php8.1                        # ignore the error if it is already gone
sudo a2enmod php8.3
sudo systemctl restart apache2

Apache only loads one mod_php at a time. If two are enabled it refuses to start, which is the fourth symptom in the table above.

Step 4: check#

Terminal
echo '<?php phpinfo();' | sudo tee /var/www/html/info.php
curl -s http://localhost/info.php | grep -o 'PHP Version [0-9.]*' | head -1
sudo rm /var/www/html/info.php               # do not leave this on a public server

If curl prints a version, Apache is running PHP again. Test in a browser too; a cached download response can make it look still broken. A hard refresh sorts that out.

If you use PHP-FPM instead#

Many setups run PHP as a separate FPM service rather than an Apache module. Then the failure looks like a 503 or a “Service Unavailable” page, and the fix is on the FPM side:

Terminal
sudo apt install php8.3-fpm
sudo systemctl enable --now php8.3-fpm
sudo a2enmod proxy_fcgi setenvif
sudo a2disconf php8.1-fpm
sudo a2enconf php8.3-fpm
sudo systemctl restart apache2
systemctl status php8.3-fpm

The socket path changes with the version too, so any virtual host that hard-codes /run/php/php8.1-fpm.sock needs updating to php8.3-fpm.sock.

Apache will not start#

Terminal
sudo apachectl configtest
sudo journalctl -u apache2 -n 30 --no-pager

Look for a line naming a .so file it cannot find. That is a stale .load file in mods-enabled. Disable it with a2dismod, or remove the symlink by hand, and try again.

Missing extensions#

A blank page after the module is working usually means the code needs an extension the upgrade did not carry across: mysqli, mbstring, curl, gd, xml, zip. Check the error log rather than guessing:

Terminal
sudo tail -n 30 /var/log/apache2/error.log
php -m | sort                                 # what the CLI has loaded
sudo apt install php8.3-mysql php8.3-mbstring php8.3-curl php8.3-xml php8.3-zip php8.3-gd
sudo systemctl restart apache2

Note that php -m shows the command-line PHP’s extensions, which can differ from Apache’s. The phpinfo page is authoritative for what the web server sees.

Keeping several PHP versions#

If you need an older PHP for one application, the ondrej/php repository on Ubuntu provides every supported version side by side. Then update-alternatives picks the CLI default and a2enmod picks Apache’s:

Terminal
sudo update-alternatives --config php
sudo a2dismod php8.3 && sudo a2enmod php8.1 && sudo systemctl restart apache2

Questions people ask#

Why did the upgrade not handle this?

Distribution upgrades replace packages but do not rewrite your Apache configuration. The enabled-modules list is your configuration, so it is left alone.

Does this apply to nginx?

nginx always uses PHP-FPM, so the FPM section applies: install the new FPM package and update the socket path in your server block.

The site works on port 80 but not over HTTPS.

That is a separate virtual host. Check the SSL host’s config for a hard-coded FPM socket path or a stale handler line.

Is mod_php or PHP-FPM better?

FPM for anything beyond a single small site. It isolates PHP from Apache, uses memory better, and lets you run different PHP versions for different sites.

Where to go next#

PHP errors not showing: turning on display_errors safelyRead next

Keep reading

PHP

Arrays and Data Structures in PHP

PHP arrays are both lists and dictionaries. How indexed and associative arrays differ, the functions worth knowing, and when to reach for…

3 min read

Keep going — pick your next guide

The fastest way to improve is to read one guide, then build the thing it describes. Start with the basics, or jump straight to a project.

Ask a question or share what worked

Your email address will not be published. Required fields are marked *