Redirect all pages except one to homepage via .htaccess

I just trasformed an old website to a one-page WordPress.

The old website had several URLS, now there’s ony 1: the homepage.
Plus another page with privacy policy.

Hence I would like to redirect all URLS except the one with slug “privacy” to the homepage.

This is what I’m trying, without success:

#custom redirects
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_URI} !^/privacy-policy/
RewriteRule (.*) http://www.mywebsite.com/$1 [R=301,L]
</IfModule>

# BEGIN WordPress (standard wp htaccess rules)
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>
# END WordPress

I get too many redirect: all urls redirect infinitely to themselves, why?

would be better (if possibile) using a redirect rule instead of a rewrite?

UPDATE

following comments suggestion I tried the following custom redirects (before WP standard rules):

<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_URI} !^/privacy-policy$
RewriteRule (.*) http://mywebsite.com/ [R=301,L]
</IfModule>

But still not working

Solutions Collecting From Web of "Redirect all pages except one to homepage via .htaccess"

Problems

I get too many redirect: all urls redirect infinitely to themselves, why?

Apache rewrite rules are processed top-to-bottom, so your custom rewrite rule is always processed first. The R redirect flag instructs Apache to tell the browser to make a new request to the altered URL instead of serving the destination of the rewrite at the current address, and the L “last” flag tells Apache to ignore all proceeding rewrite rules – so whenever your RewriteRule is applied, a new request is made and your rules are processed once again from the top.

Your RewriteCond instructs Apache to process the proceeding RewriteRule whenever the request URI path does not begin with /privacy-policy – so virtually every request is resulting in a redirect before any of the other rules in your .htaccess can be so much as evaluated.

As @MarkKaplun alluded to in the comments $1 is a back-reference that’s substituted with the portion of the matched URI contained in the first “capture-group” (the first set of parenthesis in the pattern). Since .* literally matches any number of any characters, in your RewriteRule $1 is substituted with the entire URI path.

In essence, your rewrite rules:

RewriteCond %{REQUEST_URI} !^/privacy-policy/
RewriteRule (.*) http://www.mywebsite.com/$1 [R=301,L]

evaluate to “If the URI does not begin with /privacy-policy/, have the web-browser send a new request to http://www.mywebsite.com/(original URI path).

So, for illustrative purposes, say you navigated to http://www.mywebsite.com/blog/2015; here’s what happens with each request:

  1. /blog/2015 does not start with /privacy-policy/, tell the browser to go to http://www.mywebsite.com/blog/2015.
  2. /blog/2015 does not start with /privacy-policy/, tell the browser to go to http://www.mywebsite.com/blog/2015.
  3. /blog/2015 does not start with /privacy-policy/, tell the browser to go to http://www.mywebsite.com/blog/2015.
  4. […]

Removing the $1 back-reference from the RewriteRule is insufficient, as a URI path of / (or a simply empty one) still does not qualify as beginning with /privacy-policy/, resulting the in the behavior:

  1. /blog/2015 does not start with /privacy-policy/, tell the browser to go to http://www.mywebsite.com/.
  2. / does not start with /privacy-policy/, tell the browser to go to http://www.mywebsite.com/.
  3. / does not start with /privacy-policy/, tell the browser to go to http://www.mywebsite.com/.
  4. […]

You can verify this behavior by taking a look at your Apache installation’s log files.


Solution

If you don’t plan on using WordPress’s intended address routing, there’s no reason to keep the default rewrite rules and stack more on top of them. Tailor them to your needs instead of adding complexity:

<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^(/|index\.php)$ /index.php? [L]
RewriteRule ^/?privacy-policy/?$ /index.php [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /? [L,R=301]
</IfModule>

In summary,

  1. .

    RewriteRule ^(/|index\.php)$ /index.php? [L]

    If / or index.php was explicitly requested, serve index.php, but drop the querystring (in order to prevent changing pages via query-var manipulation) and stop processing rewrites.

  2. .

    RewriteRule ^/?privacy-policy/?$ /index.php [L]

    If privacy-policy (with or without leading and trailing slashes) was explicitly requested, serve index.php and stop processing rewrites.

  3. .
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule . /? [L,R=301]
    

    If the requested filename is not a file and not a directory, match a single character anywhere (in order to apply the rule no matter the URI) and tell the browser to make a new request to / and discard it’s querystring. Stop processing rewrites.

It’s worth noting that your intended outcome will probably produce a lot of unexpected behaviors – you’ll likely be unable to access the administrative dashboard, for one. Attachments may also break. But the above should at the very least provide a good starting point for learning more about rewrite directives.

Please try

<IfModule mod_rewrite.c>
RewriteRule ^/privacy-policy/? privacy-policy [L,NC]

RewriteCond %{HTTP_HOST} ^(www\.)?oldsite\.com  [NC]
RewriteRule ^ http://mywebsite.com%{REQUEST_URI} [NE,R=301,L]
</IfModule>