Every time I set up a new deployment pipeline, I end up moving Apache’s document root away from the default /var/www/html — either to match a build output path, a dedicated partition, or a directory structure that supports multiple sites cleanly. It’s a simple change on paper, but I’ve hit the same handful of gotchas often enough that I wanted to write down exactly how I do it.
What Is the Document Root?
The DocumentRoot directive tells Apache which directory on disk maps to the root URL path (/) for a given virtual host. A request for https://example.com/about.html resolves to <DocumentRoot>/about.html on disk.
Prerequisites
- Apache installed and running
- Root or sudo access
- The new target directory ready (or about to be created)
- Whether SELinux is enforcing on my system (RHEL/CentOS), since that changes the steps I need
I check my current document root first:
apachectl -S | grep -i "port 80"
grep -r DocumentRoot /etc/apache2/sites-enabled/ # Debian/Ubuntu
grep -r DocumentRoot /etc/httpd/conf.d/ # RHEL/CentOS
Step 1: Create the New Directory
sudo mkdir -p /var/www/mysite
I always drop in a test file to confirm the change later:
echo "<h1>New document root is working</h1>" | sudo tee /var/www/mysite/index.html
Step 2: Set Correct Ownership and Permissions
sudo chown -R www-data:www-data /var/www/mysite # Debian/Ubuntu
sudo chown -R apache:apache /var/www/mysite # RHEL/CentOS
sudo chmod -R 755 /var/www/mysite
Wrong ownership is, hands down, the most common cause of a “403 Forbidden” I run into right after moving a document root.
Step 3: Update the Virtual Host Configuration
Debian/Ubuntu — I edit the relevant site config, typically /etc/apache2/sites-available/000-default.conf:
<VirtualHost *:80>
ServerName example.com
DocumentRoot /var/www/mysite
<Directory /var/www/mysite>
Options -Indexes +FollowSymLinks
AllowOverride All
Require all granted
</Directory>
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
RHEL/CentOS — I edit /etc/httpd/conf/httpd.conf or a file under /etc/httpd/conf.d/:
DocumentRoot "/var/www/mysite"
<Directory "/var/www/mysite">
Options -Indexes +FollowSymLinks
AllowOverride All
Require all granted
</Directory>
I always remember the <Directory> block — changing DocumentRoot alone isn’t enough. Apache needs explicit access permission for the new path, or it returns 403 Forbidden even with the filesystem permissions set correctly.
Step 4: Test Configuration and Restart
sudo apachectl configtest
sudo systemctl restart apache2 # or httpd on RHEL
I verify with a quick curl:
curl -I http://localhost/
SELinux Considerations (RHEL/CentOS/Fedora)
On RHEL-based systems with SELinux enforcing, moving the document root outside /var/www blocks Apache from serving files even with correct Unix permissions, because the new directory doesn’t yet have the right SELinux context.
I check SELinux status:
getenforce
Then apply the correct context:
sudo semanage fcontext -a -t httpd_sys_content_t "/var/www/mysite(/.*)?"
sudo restorecon -Rv /var/www/mysite
If semanage isn’t installed:
sudo dnf install policycoreutils-python-utils
I verify the context:
ls -Zd /var/www/mysite
I want to see httpd_sys_content_t in the output.
Moving the Document Root to a Custom Partition
For sites storing a lot of user-uploaded content, I’ve put the document root on a separate mounted volume:
sudo mkdir -p /mnt/webdata/mysite
sudo chown -R www-data:www-data /mnt/webdata/mysite
I update /etc/fstab so the volume mounts automatically at boot, then point DocumentRoot at the mounted path as above. SELinux contexts still apply even on a separate mount, so I don’t skip that step.
Real-World Use Cases
- Standardizing deployment paths to match a CI/CD pipeline’s expected output directory.
- Separating application code from static content, with the document root pointing only at a
public/subdirectory (common with PHP frameworks like Laravel). - Hosting multiple sites, each with its own document root under a shared parent.
- Using a dedicated storage volume for high-capacity or high-I/O sites.
Mistakes I’ve Made
- Changing
DocumentRootbut forgetting the matching<Directory>block, ending up staring at a403 Forbidden. - Wrong file ownership after copying files in as root, leaving root-owned files Apache’s less-privileged user can’t read.
- Forgetting SELinux context on RHEL-based systems, getting
403 Forbiddendespite everything else looking right. - Leaving the old default document root’s
<Directory>block still granting broad access to a directory nobody uses anymore. - Skipping
apachectl configtestbefore restarting, which caused downtime once over a syntax typo.
Security Best Practices
- I never set the document root to a directory containing sensitive files (
.git,.env, backups, configs) unless I’ve explicitly denied those specific files via<FilesMatch>rules. Options -Indexesstays on to prevent directory listing of the new root.- I stick to least privilege on file ownership — no
chmod -R 777shortcuts. - For framework projects with a
public/folder convention, I always pointDocumentRootthere, never at the project root, so source code and secrets stay out of reach.
Performance Considerations
- If I’m moving to network-attached or remote storage, I benchmark I/O latency first — NFS-backed document roots can add noticeable latency under high request volume.
- Local SSD-backed storage is my preference over network storage for the document root on high-traffic sites.
- I make sure the new path doesn’t cross filesystem boundaries that complicate
FollowSymLinksorOptionsbehavior.
Troubleshooting
403 Forbidden after changing DocumentRoot I check three things in order: filesystem permissions (ls -l), the <Directory> access block for the new path, and SELinux context on RHEL-based systems (ls -Zd).
404 Not Found for all pages I confirm the new directory actually has the expected files and that DocumentRoot points to the correct absolute path — typos happen more often than I’d like to admit.
Old site still loads instead of the new one I make sure I edited the config file that’s actually enabled (a2ensite on Debian/Ubuntu) and reloaded Apache; multiple virtual host files can conflict if I’m not careful.
FAQs
Do I need to restart Apache or is a reload enough? A reload (systemctl reload apache2) is usually enough for DocumentRoot changes and avoids dropping active connections, though a full restart works too.
Can different virtual hosts have different document roots on the same server? Yes — each <VirtualHost> block can define its own DocumentRoot. It’s the standard way I host multiple sites on one Apache instance.
Will changing the document root affect existing .htaccess rules? .htaccess files are read from the directory they physically live in, so as long as I move them along with the site files, they keep working, provided AllowOverride is still enabled for that directory.
Summary and Key Takeaways
- Changing the document root means updating
DocumentRoot, the matching<Directory>access block, filesystem ownership/permissions, and — on RHEL-based systems — the SELinux context. - A
403 Forbiddenafter this change is almost always permissions or access control, not a DNS or network issue. - Framework-based apps should point the document root at their
public/directory, never the project root. - I always run
apachectl configtestbefore reloading or restarting Apache.
References
- Apache DocumentRoot Directive: https://httpd.apache.org/docs/current/mod/core.html#documentroot
- Apache Directory Directive: https://httpd.apache.org/docs/current/mod/core.html#directory
- Red Hat SELinux and Apache Guide: https://access.redhat.com/documentation/
