How to Install and Enable mod_rewrite in Apache

How to install and enable mod_rewrite in Apache

If you’ve ever wanted clean URLs on your website — something like /blog/my-post instead of /index.php?id=123 — you’ve probably run into the name mod_rewrite. I remember the first time I needed it. I was setting up a WordPress site by hand on a fresh Ubuntu server, and every “pretty permalink” gave me a 404. The culprit, as always, was that mod_rewrite wasn’t enabled. In this post, I’ll walk you through exactly how I install and enable mod_rewrite on Apache, how I verify it’s working, and the mistakes I made along the way so you don’t have to repeat them.

What Is mod_rewrite and Why It Matters

mod_rewrite is an Apache module that lets you rewrite URLs on the fly using rule-based pattern matching (regular expressions). It’s the backbone of “pretty URLs” for almost every modern CMS and framework — WordPress, Laravel, Magento, Drupal, you name it. Without it, you’re stuck with ugly query-string URLs, and a lot of routing-based frameworks simply won’t function.

I use mod_rewrite for:

  • Converting query-string URLs into SEO-friendly slugs
  • Redirecting HTTP to HTTPS
  • Forcing www or non-www versions of a domain
  • Routing all requests through a single front controller (like index.php)
  • Blocking bad bots or hotlinking based on referrer patterns

Prerequisites

Before I start, I make sure I have:

  • A Linux server with Apache already installed (Ubuntu/Debian or CentOS/RHEL)
  • Root or sudo access
  • Apache running and reachable (test with systemctl status apache2 or httpd)
  • Basic comfort with the command line and a text editor like nano or vim

Step 1: Check If mod_rewrite Is Already Installed

On most distributions, mod_rewrite ships with the core Apache package, it just isn’t enabled by default. I check with:

apache2ctl -M | grep rewrite

On CentOS/RHEL systems using httpd, I run:

httpd -M | grep rewrite

If you see rewrite_module (shared) in the output, it’s already enabled and you can skip to the testing section. If you get nothing back, it means the module exists but isn’t turned on (Debian/Ubuntu), or it might not be loaded in the config at all (CentOS/RHEL).

Step 2: Enable mod_rewrite on Ubuntu/Debian

Debian-based systems use a handy helper script called a2enmod that manages module symlinks for you. I run:

sudo a2enmod rewrite

This creates a symlink from /etc/apache2/mods-available/rewrite.load into /etc/apache2/mods-enabled/. Once that’s done, I restart Apache to apply the change:

sudo systemctl restart apache2

I always double check with apache2ctl -M | grep rewrite afterward, just to be safe.

Step 3: Enable mod_rewrite on CentOS/RHEL/Fedora

On these systems, Apache is usually installed via httpd, and mod_rewrite is typically compiled in but needs to be uncommented in the config. I open the main config file:

sudo nano /etc/httpd/conf/httpd.conf

I search for this line:

#LoadModule rewrite_module modules/mod_rewrite.so

And I remove the # at the beginning so it reads:

LoadModule rewrite_module modules/mod_rewrite.so

Then I save the file and restart the service:

sudo systemctl restart httpd

Step 4: Allow .htaccess Overrides

Enabling the module alone isn’t enough. Apache also needs permission to let .htaccess files override rewrite behavior at the directory level. By default, Apache sets AllowOverride None, which silently ignores your rewrite rules even if the module is loaded — this tripped me up more than once early on.

I edit my virtual host file (commonly at /etc/apache2/sites-available/000-default.conf on Debian, or inside /etc/httpd/conf.d/ on CentOS) and add or update the <Directory> block:

<Directory /var/www/html>
    Options Indexes FollowSymLinks
    AllowOverride All
    Require all granted
</Directory>

The key line here is AllowOverride All. Without it, any rules inside your .htaccess file are ignored.

Step 5: Restart Apache and Test

After making any of these changes, I always restart Apache:

sudo systemctl restart apache2   # Debian/Ubuntu
sudo systemctl restart httpd     # CentOS/RHEL

To confirm mod_rewrite is really working, I create a simple .htaccess file in my web root:

RewriteEngine On
RewriteRule ^hello$ /index.html [L]

If visiting http://yourdomain.com/hello loads your index.html file, mod_rewrite is functioning correctly.

A Simple Real-World Example

Here’s a rule I frequently use to strip .php extensions from URLs for a cleaner look:

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^([a-zA-Z0-9_-]+)$ $1.php [L]

This checks that the requested file doesn’t already exist as-is (!-f), and if a visitor requests /about, Apache internally serves about.php without changing the URL in the browser.

Troubleshooting Common Issues

Rewrite rules not taking effect at all This is almost always one of two things: the module isn’t actually enabled, or AllowOverride is set to None. Run apache2ctl -M | grep rewrite and check your virtual host config again.

500 Internal Server Error after adding rules This usually means a syntax error in your .htaccess file. I check the Apache error log:

sudo tail -f /var/log/apache2/error.log

Infinite redirect loops This happens when a rewrite rule matches its own output. I add the RewriteCond to exclude already-rewritten requests, or I add proper [L] (last) flags to stop rule processing at the right point.

.htaccess changes not picked up Apache caches directory configuration in some setups. A full restart (not just reload) sometimes resolves this: sudo systemctl restart apache2.

Security Best Practices

  • I avoid putting overly broad AllowOverride All directives on directories that don’t need .htaccess support — it adds a small performance overhead and expands the attack surface.
  • I always test rewrite rules on a staging environment before pushing to production, since a bad rule can break an entire site’s routing.
  • I keep my .htaccess files version-controlled so I can trace back any regressions.

Performance Optimization Tips

  • If I don’t need per-directory .htaccess overrides, I move rewrite rules directly into the virtual host configuration instead. This avoids Apache having to scan the filesystem for .htaccess files on every request, which is measurably faster.
  • I keep rule sets as short and specific as possible — overly generic regex patterns can slow down request processing on high-traffic sites.

Advanced Rewrite Patterns I Use Regularly

Once the basics are working, I lean on a handful of patterns constantly. Redirecting an entire site to HTTPS:

RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

Forcing www onto every request:

RewriteCond %{HTTP_HOST} !^www\. [NC]
RewriteRule ^(.*)$ https://www.%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

Routing everything through a single front controller, which is exactly what frameworks like Laravel or Symfony expect:

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^ index.php [L]

I keep these snippets in a personal notes file because I end up reusing almost all of them on nearly every new project, tweaking the details for whatever framework or CMS I’m working with that day.

How I Debug Rewrite Rules When Nothing Else Works

When a rule set gets complicated and I genuinely can’t figure out why it isn’t matching, I turn on trace-level logging using the LogLevel directive, since the old RewriteLog directive was removed in Apache 2.4:

LogLevel alert rewrite:trace3

This writes very detailed rewrite decisions to the error log, showing exactly which conditions matched and which rule ultimately fired, condition by condition. I only leave this on temporarily during active debugging, since trace-level logging is verbose and will bloat log files quickly if left running in production. Once I’ve found the issue, I revert LogLevel back to its normal setting immediately.

I also lean on a plain text editor with regex testing built in, or an online regex tester, whenever a pattern isn’t matching the way I expect. Apache’s rewrite conditions use POSIX extended regular expressions, which behave slightly differently from PCRE in some edge cases, so testing the exact pattern against real sample URLs before dropping it into a config file saves a lot of guesswork.

Why I Prefer Virtual Host Rules Over .htaccess When Possible

Even though .htaccess files are convenient, I try to migrate rewrite rules into the main virtual host configuration whenever I have full server access. Apache has to check for .htaccess files in every parent directory of every request when AllowOverride is enabled, which adds filesystem overhead on every single page load. Rules defined directly in the virtual host are read once when Apache starts and don’t carry that repeated lookup cost. For shared hosting environments where you don’t have access to the main config, .htaccess remains the only practical option, and that’s a perfectly reasonable trade-off for the convenience it provides.

Frequently Asked Questions

Does mod_rewrite work with Nginx too? No, mod_rewrite is Apache-specific. Nginx uses its own rewrite syntax within server blocks.

Do I need mod_rewrite for WordPress permalinks? Yes. WordPress’s “pretty permalinks” feature depends entirely on mod_rewrite being enabled and AllowOverride All being set.

Can I use mod_rewrite without .htaccess? Absolutely, and it’s often better for performance to place rules directly in your virtual host config.

What Apache version do I need? mod_rewrite has been part of Apache since the 1.x days and is available in all modern 2.x releases.

Summary and Key Takeaways

Getting mod_rewrite running comes down to three things: making sure the module is loaded, making sure AllowOverride permits it in your directory config, and restarting Apache after every change. Once it’s working, it opens the door to clean URLs, redirects, and a huge amount of routing flexibility that most modern web applications rely on.

References

Total
1
Shares

Leave a Reply

Previous Post
How to troubleshoot virtual host configuration issues

How to Troubleshoot Virtual Host Configuration Issues

Next Post
How to use mod_proxy for load balancing with Apache

How to Use mod_proxy for Load Balancing with Apache

Related Posts