Apache + PHP-FPM + MP4 Streaming Simple Setup

Step 1: Install Required Packages

Why: Installs Apache, PHP, and PHP-FPM (the fast process manager).

# Update package lists
sudo apt update

# Install software-properties-common (needed to add PPAs)
sudo apt install software-properties-common apt-transport-https -y

# Add Ondřej Surý's PPA for latest Apache
sudo add-apt-repository ppa:ondrej/apache2 -y

# Add Ondřej Surý's PPA for latest PHP (if not already added)
sudo add-apt-repository ppa:ondrej/php -y

# Update again to include the new repositories
sudo apt update

# Install Apache and PHP with extensions
sudo apt install apache2 php8.3 php8.3-fpm php8.3-common php8.3-mysql \
    php8.3-xml php8.3-curl php8.3-gd php8.3-mbstring php8.3-zip \
    php8.3-bcmath php8.3-intl php8.3-soap php8.3-opcache -y

# Enable required Apache modules for PHP-FPM (if using FPM)
sudo a2enmod proxy_fcgi setenvif
sudo a2enconf php8.3-fpm

# Restart Apache
sudo systemctl restart apache2

After install add this end of the apache2.conf in the /etc/apache2

# ----------------------------------------------------------------------
# MP4 Streaming Optimizations (Safe for Prefork + mod_php)
# ----------------------------------------------------------------------

# Use kernel-level file sending (critical for video)
EnableSendfile On

# Keep connections open for seeking
MaxKeepAliveRequests 0
KeepAliveTimeout 15

# Skip ETag calculation for static files
FileETag None

# Don't compress video files (they're already compressed)
<IfModule mod_deflate.c>
    SetEnvIfNoCase Request_URI \.mp4$ no-gzip dont-vary
</IfModule>

# Browser caching for videos
<IfModule mod_expires.c>
    ExpiresActive On
    ExpiresByType video/mp4 "access plus 1 month"
</IfModule>

# Skip logging video requests (saves disk I/O)
SetEnvIf Request_URI "\.mp4$" dontlog

To restart both php and apache

sudo systemctl restart php8.3-fpm
sudo systemctl restart apache2


Step 2: Enable Apache Modules

Why: Activates features for caching, compression, URL rewriting, SSL, and PHP-FPM connection.

sudo a2enmod headers expires rewrite ssl proxy proxy_fcgi setenvif

Step 3: Switch from Prefork to Event MPM

Why: Prefork = 1 process per user (slow, memory heavy). Event = 1 process handles many users (fast, efficient for streaming).

sudo a2dismod php8.3
sudo a2dismod mpm_prefork
sudo a2enmod mpm_event
sudo a2enconf php8.3-fpm

After that it is a good idea to setup the mpm_worker.conf in the /etc/apache2/mods-enabled

StartServers            3
MinSpareThreads         64
MaxSpareThreads         128
ThreadLimit             64
ThreadsPerChild         64
MaxRequestWorkers       256
MaxConnectionsPerChild  1000


Step 4: Start PHP-FPM

Why: PHP-FPM runs separately from Apache. Must be running or PHP won’t work.

sudo systemctl enable php8.3-fpm
sudo systemctl start php8.3-fpm
  1. /etc/php/8.3/apache2/php.ini
    • Used when PHP runs as an Apache module (mod_php)
    • Handles PHP via Apache directly
  2. /etc/php/8.3/fpm/php.ini
    • Used when PHP runs via PHP-FPM (FastCGI Process Manager)
    • Handles PHP as a separate process that Apache communicates with

Step 5: Add Streaming Optimizations to /etc/apache2/apache2.conf

Why: Makes video streaming faster by using kernel-level file transfer, keeping connections open for seeking, and skipping unnecessary processing.

# Kernel sends file directly to network (bypasses Apache buffer)
EnableSendfile On

# Keeps connection open so user can seek without reconnecting
MaxKeepAliveRequests 0
KeepAliveTimeout 15

# Skips unnecessary file checksum calculation
FileETag None

# MP4 is already compressed, don't waste CPU compressing again
<IfModule mod_deflate.c>
    SetEnvIfNoCase Request_URI \.mp4$ no-gzip dont-vary
</IfModule>

# Tells browser to cache videos for 1 month
<IfModule mod_expires.c>
    ExpiresActive On
    ExpiresByType video/mp4 "access plus 1 month"
</IfModule>

# Stops logging every video chunk request (saves disk I/O)
SetEnvIf Request_URI "\.mp4$" dontlog
CustomLog ${APACHE_LOG_DIR}/access.log combined env=!dontlog

Step 6: Virtual Host PHP Handler

Why: Tells Apache to send PHP files to PHP-FPM instead of trying to process them itself.

<FilesMatch \.php$>
    SetHandler "proxy:unix:/run/php/php8.3-fpm.sock|fcgi://localhost"
</FilesMatch>

Step 7: Restart Everything

Why: Applies all changes.

sudo systemctl restart php8.3-fpm
sudo systemctl restart apache2

Step 8: Verify

Why: Confirms everything is working.

apachectl -V | grep -i mpm
sudo systemctl status php8.3-fpm

Extras:

Install btop and iftop to watch resources and network use easier.

apt install iftop

btop need couple steps read their install docs

https://github.com/aristocratos/btop?tab=readme-ov-file#with-cmake-community-maintained

Disable gzip for videos since they are already encoded and compressed VirtualHost setup.

Disable logs since every network log will eat the I/O resource that server needs.

For security to block hotlinking and not allowing people t odownload your videos easily.

Headers setup in the vhost or in the cloudflare.

Header always set Access-Control-Allow-Origin "https://yoursite.com"
Header always set Access-Control-Allow-Methods "GET, HEAD, OPTIONS"
Header always set Access-Control-Allow-Headers "Content-Type, Range, Accept-Encoding"
Header always set Access-Control-Expose-Headers "Content-Length, Content-Range, Accept-Ranges"
Header always set Access-Control-Allow-Credentials "true"