.htaccess rules - Too many redirects - apache

I'm trying to accomplish 2 things with htaccess file:
Force https on all but 2 directories, only if accessed from prod url (no https on dev)
Redirect any nonexisting url in index.php
this is what I have:
RewriteEngine On
# Redirect all http to https
RewriteCond %{HTTPS} !=on
RewriteCond %{HTTP_HOST} myserver\.com$ [NC]
RewriteRule ^(api|images)($|/) - [L]
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
# The following rule tells Apache that if the requested filename
# exists, simply serve it.
RewriteCond %{REQUEST_FILENAME} -s [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^.*$ - [NC,L]
# The following rewrites all other queries to index.php. The
# condition ensures that if you are using Apache aliases to do
# mass virtual hosting, the base path will be prepended to
# allow proper resolution of the index.php file; it will work
# in non-aliased environments as well, providing a safe, one-size
# fits all solution.
RewriteCond %{REQUEST_URI}::$1 ^(/.+)(.+)::\2$
RewriteRule ^(.*) - [E=BASE:%1]
RewriteRule ^(.*)$ %{ENV:BASE}index.php [NC,L]
When I add the first bit (the https enforce) I get "too many redirects" error. Any Idea why?

A RewriteCond is only applicable to next RewriteRule. Your redirect rule for http->https is unconditional and will cause a redirect loop.
Have it this way:
RewriteEngine On
# Redirect all http to https
RewriteCond %{HTTPS} !=on
RewriteCond %{HTTP_HOST} myserver\.com$ [NC]
RewriteCond %{THE_REQUEST} !\s/+(?:api|images)[/?\s] [NC]
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301,NE]
# The following rule tells Apache that if the requested filename
# exists, simply serve it.
RewriteCond %{REQUEST_FILENAME} -s [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]
# The following rewrites all other queries to index.php. The
# condition ensures that if you are using Apache aliases to do
# mass virtual hosting, the base path will be prepended to
# allow proper resolution of the index.php file; it will work
# in non-aliased environments as well, providing a safe, one-size
# fits all solution.
RewriteCond %{REQUEST_URI}::$1 ^(/.+)(.+)::\2$
RewriteRule ^(.*) - [E=BASE:%1]
RewriteRule ^ %{ENV:BASE}index.php [L]

I believe this part should look more like:
RewriteRule ^(api|images)($|/) - [L]
RewriteCond %{HTTPS} !=on
RewriteCond %{HTTP_HOST} myserver\.com$ [NC]
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

Related

.htaccess rules to redirect crawlers to another folder

I have a single page app (react, react-router) and I am trying to redirect search and share bots/crawlers to a different folder with static snapshots of the app. I have a .htaccess setup which is mostly working but missing a few things.
It is currently correctly redirecting from domain.com/something to domain.com/snap/something/
Edit: It is now correctly redirecting domain.com to domain.com/snap/
It does not handle domain.com/non-existing, it should redirect to domain.com/snap/404.html but currently redirects to domain.com/snap/non-existing
The .htaccess I have is
<IfModule mod_rewrite.c>
Options +FollowSymlinks
RewriteEngine On
RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
RewriteRule ^(.*)$ https://%1/$1 [L,R=301]
# For crawlers show snapshots
RewriteCond %{HTTP_USER_AGENT} googlebot|bingbot|yandex|baiduspider|facebookexternalhit|twitterbot|rogerbot|linkedinbot|embedly|quora\ link\ preview|showyoubot|outbrain|pinterest|slackbot|vkShare|W3C_Validator [NC,OR]
RewriteCond %{QUERY_STRING} _escaped_fragment_
RewriteCond %{REQUEST_URI} !^/(snap) [NC]
# Proxy the request
RewriteRule ^(?!.*?(\.js|\.css|\.xml|\.less|\.png|\.jpg|\.jpeg|\.gif|\.pdf|\.doc|\.txt|\.ico|\.rss|\.zip|\.mp3|\.rar|\.exe|\.wmv|\.doc|\.avi|\.ppt|\.mpg|\.mpeg|\.tif|\.wav|\.mov|\.psd|\.ai|\.xls|\.mp4|\.m4a|\.swf|\.dat|\.dmg|\.iso|\.flv|\.m4v|\.torrent|\.ttf|\.woff))(.*) /snap/$2/ [R=301,L]
# Don't rewrite files or directories
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]
# If the requested pattern is file and file doesn't exist, send 404
RewriteCond %{REQUEST_URI} ^(\/[a-z_\-\s0-9\.]+)+\.[a-zA-Z]{2,4}$
RewriteRule ^ - [L,R=404]
# otherwise use history router
RewriteRule ^ /index.html [L]
</IfModule>
Edit: I moved the crawler part of .htaccess up and it is now correctly handling domain.com -> domain.com/snap/ scenario, but I can't figure out how to make it handle the non-existing routes.
To redirect your 404 uris to the correct destination, you can use the following rule . Put this right bellow your www to non-www redirect rule block and make sure to clear your browser cache before testing :
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{HTTP_USER_AGENT} googlebot|bingbot|yandex|baiduspider|facebookexternalhit|twitterbot|rogerbot|linkedinbot|embedly|quora\ link\ preview|showyoubot|outbrain|pinterest|slackbot|vkShare|W3C_Validator [NC,OR]
RewriteCond %{QUERY_STRING} _escaped_fragment_
RewriteCond %{REQUEST_URI} !^/(snap) [NC]
# Proxy the request
RewriteRule ^(?!.*?(\.js|\.css|\.xml|\.less|\.png|\.jpg|\.jpeg|\.gif|\.pdf|\.doc|\.txt|\.ico|\.rss|\.zip|\.mp3|\.rar|\.exe|\.wmv|\.doc|\.avi|\.ppt|\.mpg|\.mpeg|\.tif|\.wav|\.mov|\.psd|\.ai|\.xls|\.mp4|\.m4a|\.swf|\.dat|\.dmg|\.iso|\.flv|\.m4v|\.torrent|\.ttf|\.woff))(.*) /snap/404.html [R=301,L]

.htaccess: Redirect 301 conflict with rewriterule

I recently updated an old website and I want to 301 all the old pages to the new version of each of them.
Here is my .htaccess
Options +FollowSymlinks -MultiViews
RewriteEngine On
#
# below are the 301 redirects
# **rules**
#
# below are the RewriteCond
# **rules**
#
# other stuff
#
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule .* - [L]
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^([^\.]+?)/?$ $1.php [NC,L]
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}\.php -f
RewriteCond %{THE_REQUEST} ^(.+)\.php([#?][^\ ]*)?\ HTTP/
RewriteRule ^(.+)\.php$ /$1 [R=301,L]
The problem is that some of the old link are with the extension declared oldfile.html, others are just folders oldfolder/.
On the new version there are products, products/category and products/category/subcategory. These urls are generated with RewriteCond.
The following rule works:
Redirect 301 /category/ /products/category
The following does not work and instead redirects me to the home page.
Redirect 301 /old-folder/subcategory.html /products/category/subcategory
# /products/category/subcategory is also a RewriteRule
Here is the exact code in the .htaccess
# 301 rule
Redirect 301 /arredo-urbano/blocchi-standard.html /prodotti/arredo-urbano/001-blocchi-standard
# RewriteRule
RewriteRule ^prodotti/([^/]+)/([^/]+)/?$ prodotti.php?category=$1&subcategory=$2 [L]
I tried adding the [L] flag after each Redirect 301 so the rest of the .htaccess is ignored, but this causes a 500 Internal Error
All redirects are manually declared, with no regular expressions or wildcards and they are all spelled correctly.
Is there a way of making all redirect work as intended ? Also the thing that I do not understand is why the [L] flag causes an internal error.
Thank you
Three recommendations about redirect rules in general:
Keep redirect rules before internal rewrite rules
Keep specific patterns matching rules before generic pattern matching rules
Don't mix Redirect (mod_alias) rules with RewriteRule (mod_rewrite) rules.
Try these rules in your .htaccess:
Options +FollowSymlinks -MultiViews
RewriteEngine On
RewriteRule ^category/?$ /products/category [L,NC,R=301]
RewriteRule ^old-folder/subcategory\.html$ /products/category/subcategory [L,NC,R=301]
#
# below are the 301 redirects
# **rules**
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}\.php -f
RewriteCond %{THE_REQUEST} ^(.+)\.php([#?][^\ ]*)?\ HTTP/
RewriteRule ^(.+)\.php$ /$1 [R=301,L]
#
# below are the RewriteCond
# **rules**
#
# other stuff
#
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule .* - [L]
RewriteCond %{REQUEST_FILENAME}.php -f
RewriteRule ^(.+?)/?$ $1.php [L]
Make sure to clear your browser cache before testing this change.

apache - mod_rewrite - redirect some paths to https others to http

My company wants our site to force an http protocol for most of the site except for three sections 'user', 'shop', and 'cart'. I used the online .htaccess tester at http://htaccess.madewithlove.be/ to build my file first, and it appeared to be correct there. However, when I actually implemented it I had a problem. If I go to https for any of the regular pages it works and I'm redirected to the http version just fine. However if I go to '/shop', '/cart', or '/user' no matter if it's with http or https i just get redirected to the home page with http. Here's the .htaccess file:
RewriteEngion on
# Force HTTPS for /shop
RewriteCond %{HTTPS} off
RewriteCond %{REQUEST_URI} /shop
RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [NC,R=301,L]
# Force HTTPS for /cart
RewriteCond %{HTTPS} off
RewriteCond %{REQUEST_URI} /cart
RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [NC,R=301,L]
# Force HTTPS for /user
RewriteCond %{HTTPS} off
RewriteCond %{REQUEST_URI} /user
RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [NC,R=301,L]
# Force HTTP for all others
RewriteCond %{HTTPS} on
RewriteCond %{REQUEST_URI} !/cart [NC]
RewriteCond %{REQUEST_URI} !/shop [NC]
RewriteCond %{REQUEST_URI} !/user [NC]
RewriteRule (.*) http://%{HTTP_HOST}%{REQUEST_URI} [NC,R=301,L]
# Pass all requests not referring directly to files in the filesystem to
# index.php. Clean URLs are handled in drupal_environment_initialize().
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} !=/favicon.ico
RewriteRule ^ index.php [L]
You need to use THE_REQUEST variable in your conditions. THE_REQUEST variable represents original request received by Apache from your browser and it doesn't get overwritten after execution of some rewrite rules. Example value of this variable is GET /index.php?id=123 HTTP/1.1
# Force HTTPS for cart, shop, user
RewriteCond %{HTTPS} off
RewriteCond %{THE_REQUEST} \s/(cart|shop|user) [NC]
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [NE,R=301,L]
# Force HTTP for all others
RewriteCond %{HTTPS} on
RewriteCond %{THE_REQUEST} !\s/(cart|shop|user) [NC]
RewriteRule ^ http://%{HTTP_HOST}%{REQUEST_URI} [NE,R=301,L]
# Pass all requests not referring directly to files in the filesystem to
# index.php. Clean URLs are handled in drupal_environment_initialize().
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} !=/favicon.ico
RewriteRule ^ index.php [L]

How to force http:// in htaccess?

I currently have this in my htaccess file to force https on all pages on my sign in and sign up pages:
# Force https://
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteCond $1 ^(sign-in|sign-up) [NC]
RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
</IfModule>
I want to force http whenever the page is not sign in or sign up. How do I do that?
EDIT 1:
I actually need to do the following:
Force https:// for pages in sign-up and sign-in
Force http:// for pages not in sign-up and sign-in
Remove www
Remove index.php
The relevant part of my .htaccess has this:
#
# Force https:// or http://
#
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteCond $1 ^(sign-in|sign-up) [NC]
RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
#RewriteCond %{HTTPS} on
#RewriteCond $1 !^(sign-in|sign-up) [NC]
#RewriteRule (.*) http://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
#RewriteCond %{HTTPS} on
#RewriteCond %{REQUEST_URI} !^/(sign-in|sign-up)(/|$) [NC]
#RewriteRule ^ http://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
</IfModule>
#
# Supress "www" at the beginning of URLs. Source: HTML5 Boilerplate
#
<IfModule mod_rewrite.c>
RewriteEngine On
#RewriteCond %{HTTPS} !=on
RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
RewriteRule ^ http://%1%{REQUEST_URI} [R=301,L]
</IfModule>
#
# Supress "index.php"
#
<IfModule mod_rewrite.c>
RewriteEngine On
# Removes index.php from ExpressionEngine URLs
RewriteCond $1 !\.(gif|jpe?g|png)$ [NC]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ /index.php/$1 [L]
</IfModule>
Have your rules like this:
RewriteEngine On
# force HTTPS for sign-in|sign-up
RewriteCond %{HTTPS} off
RewriteRule ^(sign-in|sign-up)(/|$) https://%{HTTP_HOST}%{REQUEST_URI} [NC,L,R=301]
# force HTTP for NOT sign-in|sign-up
RewriteCond %{HTTPS} on
RewriteCond %{REQUEST_URI} !^/(sign-in|sign-up)(/|$) [NC]
RewriteRule ^ http://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
I have not implemented this kind of solution personally (I have tended to use the PHP solution, like the one above, for it's simplicity), but the following may be, at least, a good start.
RewriteEngine on
# Check for POST Submission
RewriteCond %{REQUEST_METHOD} !^POST$
# Forcing HTTPS
RewriteCond %{HTTPS} !=on [OR]
RewriteCond %{SERVER_PORT} 80
# Pages to Apply
RewriteCond %{REQUEST_URI} ^something_secure [OR]
RewriteCond %{REQUEST_URI} ^something_else_secure
RewriteRule .* https://%{SERVER_NAME}%{REQUEST_URI} [R=301,L]
# Forcing HTTP
RewriteCond %{HTTPS} =on [OR]
RewriteCond %{SERVER_PORT} 443
# Pages to Apply
RewriteCond %{REQUEST_URI} ^something_public [OR]
RewriteCond %{REQUEST_URI} ^something_else_public
RewriteRule .* http://%{SERVER_NAME}%{REQUEST_URI} [R=301,L]
You can use environment variables to track the states you need and then only issue one redirect if necessary.
The following block should do exactly what you want:
<IfModule mod_rewrite.c>
RewriteEngine On
# Define default protocol for all assets
RewriteRule ^ - [E=protocol:http,E=ssl:%{HTTPS}off]
# Define HTTPS targets, add more cond as required
RewriteCond $1 (sign-in|sign-up) [NC]
RewriteRule (.*) - [E=protocol:https,E=ssl:%{HTTPS}on]
# Rewrite host if necessary and redirect on correct protocol
RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
RewriteRule ^ %{ENV:protocol}://%1%{REQUEST_URI} [L,R=301]
# Protocol switch if necessary (host is already correct)
RewriteCond %{ENV:ssl} ^(onoff|offon)
RewriteCond %{REQUEST_URI} !\.(gif|jpe?g|png|css|js)$ [NC]
RewriteRule ^ %{ENV:protocol}://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
# Finally, process the internal redirect
RewriteCond $1 !\.(gif|jpe?g|png)$ [NC]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ /index.php/$1 [L]
</IfModule>
In this configuration, the protocol environment variable is used to track the actual protocol name to use and the ssl environment variable is the test to check if we are already on the correct protocol for the URI.
Using environment variables gives you 2 advantages :
you avoid redirection chains. This configuration will redirect the user-agent at most once.
you're DRY (Don't repeat yourself). Each condition is only expressed once, so it's easier to maintain.

Redirect parked domain (htaccess)

I have two domains in the format of foo.com and foo.net. I currently have the following in my .htaccess to redirect non-www to www:
# Rewrite URL to force WWW
RewriteCond %{HTTP_HOST} ^[^.]*\.[^.]*$
RewriteCond %{SERVER_PORT} !=443
RewriteRule ^ http://www.%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
RewriteCond %{HTTP_HOST} ^[^.]*\.[^.]*$
RewriteCond %{SERVER_PORT} =443
RewriteRule ^ https://www.%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
RewriteCond %{HTTP_HOST} ^www\.(.*\.[^.]*\.[^.]*)$ [NC]
RewriteCond %{SERVER_PORT} !=443
RewriteRule ^ http://%1%{REQUEST_URI} [R=301,L]
RewriteCond %{HTTP_HOST} ^www\.(.*\.[^.]*\.[^.]*)$ [NC]
RewriteCond %{SERVER_PORT} =443
RewriteRule ^ https://%1%{REQUEST_URI} [R=301,L]
However, I want to redirect URLs ending in .net to go to .com, including the subdomains.
Examples:
foo.net -> foo.com
www.foo.net -> www.foo.com
foo.net/bar -> foo.com/bar
sub.foo.net -> sub.foo.com
sub.foo.net/bar -> sub.foo.com/bar
I used to have this on another site, but it was years ago and I don't have the .htaccess anymore. Does anybody know an easy way of doing this?
Edit: Here is the rest of the .htaccess:
# BEGIN WordPress
#
RewriteEngine on
#
# Unless you have set a different RewriteBase preceding this point,
# you may delete or comment-out the following RewriteBase directive
# RewriteBase /
#
# if this request is for "/" or has already been rewritten to WP
RewriteCond $1 ^(index\.php)?$ [OR]
# or if request is for image, css, or js file
RewriteCond $1 \.(gif|jpg|png|php|ico|css|js)$ [NC,OR]
# or if URL resolves to existing file
RewriteCond %{REQUEST_FILENAME} -f [OR]
# or if URL resolves to existing directory
RewriteCond %{REQUEST_FILENAME} -d
# then skip the rewrite to WP
RewriteRule ^(.*)$ - [S=1]
# else rewrite the request to WP
RewriteRule . /index.php [L]
#
# END wordpress
# Rewrite URL to force WWW
-(see top of post)-
Ignoring the fact that this is by far the most devilish rewrite ruleset that I've ever written, this surprisingly seems to take care of what you currently have, plus what you want, in a nice, compact package.
Let me know if you have any problems with it:
RewriteCond %{HTTPS} =on
RewriteRule .* - [E=RW_HTTPS:s]
RewriteCond %{HTTP_HOST} ^www\.(.*\.[^.]*\.[^.]*)$ [NC,OR]
RewriteCond www.%{HTTP_HOST} ^(www\.[^.]*\.[^.]*)$
RewriteRule .* - [E=RW_THOST:%1]
RewriteCond %{ENV:RW_THOST} ^(.*)\.(net|com)$ [NC,OR]
RewriteCond %{HTTP_HOST} ^(.*)\.net$ [NC]
RewriteRule .* http%{ENV:RW_HTTPS}://%1.com%{REQUEST_URI} [R=301,L]
To Redirect Parked domain to main website domain with specific URL Try The following code on your htacess file :
RewriteEngine On
RewriteBase /
RewriteCond %{HTTP_HOST} ^parked-domain.com [OR]
RewriteCond %{HTTP_HOST} ^www.parked-domain.com
RewriteRule ^(.*)$ https://main-domain.com/custom-url [L,R=301]