Skip to content

How to Set Up a Permanent (301) Redirect in Apache: Redirect, RedirectPermanent, RedirectMatch and RewriteRule

Updated 5 min read

Updated: 8 October 2026 · Applies to: Apache HTTP Server 2.4 (tested on 2.4.69, 2.4.66 on Ubuntu 26.04 and 2.4.63 on AlmaLinux 10)

To make a permanent (301) redirect in Apache, add one line to the site’s virtual host or .htaccess file: Redirect permanent "/old-page.html" "https://www.example.com/new-page/". RedirectPermanent "/old-page.html" "https://www.example.com/new-page/" and Redirect 301 ... do exactly the same. These directives come from mod_alias, which is loaded by default. Without permanent or 301, Redirect sends a temporary 302.

1. Redirect permanent and RedirectPermanent

All three lines below send a 301:

Redirect permanent "/old-page.html" "https://www.example.com/new-page/"
RedirectPermanent "/docs" "/manual"
Redirect 301 "/old.html" "/new.html"

What we got with curl -I:

/old-page.html              HTTP/1.1 301 Moved Permanently  Location: https://www.example.com/new-page/
/docs/install.html?lang=en  HTTP/1.1 301 Moved Permanently  Location: http://localhost/manual/install.html?lang=en
/old.html?x=1               HTTP/1.1 301 Moved Permanently  Location: http://localhost/new.html?x=1
/docsfoo                    HTTP/1.1 404 Not Found

Things to know:

  • The old path must start with / and is case-sensitive.
  • It matches the start of the path, and anything after it is added to the new URL, together with the query string. /docs also redirected /docs/install.html.
  • Only whole path segments match, so /docs did not catch /docsfoo.
  • A target that starts with / stays on the same site; Apache turns it into a full URL.
  • Other status words: temp (302, the default), seeother (303) and gone (410, with no target: Redirect gone "/removed.html"). For the difference between 301 and 302, see what is a 302 redirect.

2. Move a whole site to a new domain

In the virtual host of the old domain only:

<VirtualHost *:80>
    ServerName old.example
    ServerAlias www.old.example
    Redirect permanent "/" "https://new.example/"
</VirtualHost>

Every path is kept: /any/path.html went to https://new.example/any/path.html. If you have an HTTPS virtual host for the old domain, add the same line there.

3. RedirectMatch: patterns with regular expressions

RedirectMatch 301 "^/blog/(.*)$" "https://blog.example.com/$1"

/blog/2026/hello-world went to https://blog.example.com/2026/hello-world. Anchor the pattern with ^: without it, RedirectMatch matches anywhere in the path, and /archive/blog/post was redirected as well.

4. RewriteRule with [R=301]: redirects with conditions

Use mod_rewrite when the redirect depends on something other than the path, such as the host name or HTTPS. The Apache manual recommends Redirect and RedirectMatch for simple cases.

RewriteEngine On

# www to non-www, and HTTP to HTTPS
RewriteCond "%{HTTP_HOST}" "^www\.(.+)$" [NC]
RewriteRule "^" "https://%1%{REQUEST_URI}" [R=301,L]
RewriteCond "%{HTTPS}" "!=on"
RewriteRule "^" "https://%{HTTP_HOST}%{REQUEST_URI}" [R=301,L]

# old domain to new domain (in a virtual host)
RewriteCond "%{HTTP_HOST}" "^(www\.)?old\.example$" [NC]
RewriteRule "^(.*)$" "https://new.example$1" [R=301,L]
  • R=301 makes it permanent. A plain [R] gave a 302 in our test, and R=308 gave 308 Permanent Redirect.
  • In a virtual host, the matched path starts with a slash, so write https://new.example$1. With https://new.example/$1 we got Location: https://new.example//page.
  • In .htaccess, the path is relative to that folder and has no leading slash, so /$1 is correct there.
  • If a Redirect and a RewriteRule in the same virtual host match the same URL, the RewriteRule won in our test. Use one method per URL.

More on rules and flags: how to use Apache mod_rewrite. HTTPS only: permanent redirect to HTTPS with .htaccess.

5. Virtual host or .htaccess?

The virtual host is faster and always works. .htaccess is read only if the folder allows it with AllowOverride FileInfo (or All). Ubuntu (/var/www) and AlmaLinux (/var/www/html) ship with AllowOverride None. In our tests:

  • With AllowOverride None, the .htaccess file was ignored and the old URL returned 404.
  • With only AllowOverride AuthConfig, the request failed with 500 and the error log showed .htaccess: Redirect not allowed here.

In .htaccess, Redirect still uses the full path from the site root, for example Redirect 301 "/sub/a.html" "/sub/b.html". On cPanel hosting you can also use the Redirects tool: redirect a website in cPanel.

6. Enable the module and apply the change

Ubuntu and Debian: mod_alias is on; mod_rewrite is not.

sudo a2enmod rewrite
sudo apache2ctl configtest
sudo systemctl restart apache2

AlmaLinux, Rocky Linux and RHEL: both modules are loaded by default (/etc/httpd/conf.modules.d/00-base.conf).

sudo apachectl configtest
sudo systemctl reload httpd

Always run the config test first. Typical errors:

  • Invalid command 'RewriteEngine', perhaps misspelled or defined by a module not included in the server configuration: mod_rewrite is not enabled.
  • RedirectPermanent takes two arguments, a document to be redirected, then the destination URL: the target URL is missing.

7. Test the redirect

curl -I http://www.example.com/old-page.html

Look for 301 Moved Permanently and the Location header. curl shows the server’s real answer, without a browser’s history or cache in the way. To follow a chain of redirects to the end, use curl -IL.

Official documentation: mod_alias (Redirect, RedirectMatch, RedirectPermanent), RewriteRule flags and when not to use mod_rewrite.

Related services

KVM VPS hosting

NVMe SSD and full root access, with Free Basic Managed Support.

Managed cPanel VPS

cPanel included; we handle updates, security and monitoring.

Server management

on-demand administration, hardening, migrations and monitoring.

Was this answer helpful?

Still stuck? Open a ticket