Troubleshooting: Too Many Redirects

Reading Time: 7 minutes

The error “too many redirects” means that the website keeps being redirected between different addresses in a way that will never complete. Often this is the result of competing redirects, one trying to force HTTPS (SSL) and another redirecting back to HTTP (non-SSL), or between www and non-www forms of the URL.

If you are using a CMS like WordPress, Magento, etc., that utilizes a base_url or URL type configuration within the site, you can end up with the configuration in the code or database conflicting with a redirect in a .htaccess file. These conflicting redirects will flip flop back and forth and never complete.

Your browser protects you from this by only allowing a certain number of redirects (often ten or so) before it gives up and reports the error message “too many redirects.” This shows up differently between Chrome, Firefox, and other browsers.

Firefox: The page isn’t redirecting properly. An error occurred during a connection to <domain>

The firefox error, "The page isn't redirecting properly." shows in infinite loop of happening to the domain.

Chrome: This page isn’t working <domain> redirected you too many times

The Chrome message "...redirected you too many times. " (ERR_TOO_MANY_REDIRECTS) error shows an infinite loop happening within the domain.

Even the test utility curl gives up after 50 redirects by default.

Curl: Maximum (X) redirects followed

curl -svILk https://www.example.com
....
* Maximum (50) redirects followed

The First Step: Cache and Cookies

As is shown in the browser errors above, these looping redirects can also be caused by cookies in the browser caching old redirects. The first step in testing would be to clear your cache and cookies in your browser. If you’ve already cleared cache and cookies in the browser, then it is time to move on to some more advanced troubleshooting.

Using Developer Tools for Redirect Loops

The next step in troubleshooting these kinds of redirect loops is to use the Developer Tools in Firefox or Chrome. These tools are commonly opened by pressing the F12 key. Make sure you select the Network tab in either of these and then reload the page you are having an issue with.

After reloading the page, you should see the series of redirects listed out for you in the new window. Looking at the redirects, you can see if they are redirecting between a few different things or redirecting to the same thing. Either way, you can see the steps that lead up to the error, instead of just the end user’s browser error.

Developer Tools in Firefox

The Developer Tools in Firefox are tools you can use to troubleshoot redirect errors.

DevTools in ChromeLike Firefox, Chrome also has DevTools that aid in the diagnosis of "ERR_TOO_MANY_REDIRECTS".

Using cURL for Redirect Loops

As part of writing this article, we put together a fairly simple Bash script that can be used on any Unix-like system with the curl command. Using curl is nice, because it does not cache things in the same way as browsers does, so it can sometimes give you a different perspective when troubleshooting.

Copy the following into your preferred text editor and save it as redirects.sh.

#!/bin/bash
echo
for domain in $@; do
echo --------------------
echo $domain
echo --------------------
curl -sILk $domain | egrep 'HTTP|Loc' | sed 's/Loc/ -> Loc/g'
echo
done

Then mark the redirects.sh file as executable.

chmod +x redirects.sh

You can run our script, like the examples below, by adding your domain after the script name. It can even check multiple domains and it will check the redirects of each URL, in turn, putting a header between separate domains tested.

Example Output

./redirects.sh liquidweb.com
--------------------
liquidweb.com
--------------------
HTTP/1.1 301 Moved Permanently
-> Location: https://liquidweb.com/
HTTP/1.1 301 Moved Permanently
-> Location: https://www.staging.liquidweb.com/
HTTP/1.1 200 OK

Example of an Infinite Redirect from HTTP to HTTPS

./redirects.sh http://www.example.com
--------------------
http://www.example.com
--------------------
HTTP/1.1 301 Moved Permanently
-> Location: https://www.example.com/
HTTP/1.1 301 Moved Permanently
-> Location: http://www.example.com/
HTTP/1.1 301 Moved Permanently
-> Location: https://www.example.com/
HTTP/1.1 301 Moved Permanently
-> Location: http://www.example.com/
HTTP/1.1 301 Moved Permanently
....
-> Location: https://www.example.com/
HTTP/1.1 301 Moved Permanently
-> Location: http://www.example.com/
HTTP/1.1 301 Moved Permanently
-> Location: https://www.example.com/
HTTP/1.1 301 Moved Permanently
-> Location: http://www.example.com/
HTTP/1.1 301 Moved Permanently
-> Location: https://www.example.com/

A Side Note on Redirect Types

Looking at the curl output above you can see that the HTTP response code is 301. 301 redirects are “Permanent” redirect, meaning that something has permanently moved, and you or your browser have to look it up in the new location both now and in the future. 302 redirects are “Temporary” redirects meaning that something has moved for now, but may not always be in the new location.

301 redirects are more often than not are written out as redirect or rewrite entries in a .htaccess file. 302 redirects, however, either by design or convention are often generated within the code of a website. So good rule of thumb is 301s are in .htaccess files, and 302s are in site code. This may not always be true, but it is a good thing to keep in mind.

Redirects in the .htaccess File

The .htaccess file is a configuration file used to modify Apache server behavior per directory on a website/server. This is a user-level configuration file, and only some Apache configurations can be edited here, though redirects are common use.

You can have multiple .htaccess files that cascade over a series of directories. If you have a .htaccess in a parent directory, and another in a sub-directory they will both affect the sub-directory.  In these instances, it is important to remember where you do and do not have .htaccess files, to prevent conflicts between .htaccess files at different levels.

Below are a series of redirect examples and will aid in identifying redirects in your .htaccess file. These are not the only ways to do these kinds of redirects, but these should show you what the most common redirects look like so that you can recognize them if they are in a .htaccess file you are working with.

Force HTTPS

The .htaccess code below first checks if the request came into the server using HTTP or HTTPS. If the request did not use HTTPS, then the configuration will tell the browser to redirect over to the HTTPS version of the same website and URL that was requested before.

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

Force HTTPS: When Behind a Load Balancer or Proxy (CloudFlare/Incapsula/Sucuri/etc.)

Sometimes you may be using a proxy, like a load balancer or a web firewall, like CloudFlare, Incapsula, or Sucuri. These can be configured to use SSL on the front end, but not use SSL on the back end. To allow this to work correctly, you need to check not just for HTTPS in the request, but also if the proxy passed the original HTTPS request to the server using just HTTP. This following rule checks if the request was forwarded from HTTPS, and if so does not try to redirect an additional time.

RewriteEngine On
RewriteCond %{HTTPS} off
RewriteCond %{HTTP:X-Forwarded-Proto} =http
RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]

Force non-www

This redirect only checks if the website name was requested with www at the start of the domain name. If the www is included, it rewrites the request and tells the browser to redirect over to the non-www version of the domain name.

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

Force www

This last redirect checks if the website name was not requested with www at the start of the domain name. If the www is not included, it rewrites the request and tells the browser to redirect over to the www version of the domain.

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

WordPress

The WordPress CMS uses a .htaccess file for rewriting URLs to the index.php file, but it defines the URL of the website as a value in the database. If you do not already know the name of the database that is being used on the site, you can look it up in the main configuration for WordPress (wp-config.php).

You can also open the file in a text editor and look for these values, but from an SSH connection, you can use the program grep. This gives you more than just the database name, but the database name is the most important for what we need to do next.

grep DB wp-config.php

define('DB_NAME', 'wordpress_database');
define('DB_USER', 'wordpress_username');
define('DB_PASSWORD', 'Password');
define('DB_HOST', 'localhost');
define('DB_CHARSET', 'utf8');
define('DB_COLLATE', '');

The wp_options Table

Once you know the name of the database, you can then look at the options table of the WordPress database to see what the URL is set to in the database. The options table can have any prefix at the beginning of the table name, but it is often “wp_” by default, so the full name of the options table is usually wp_options. The two lines that are important are the home and siteurl lines in the options table. You can find these using phpMyAdmin, or another database management utility, but from the command line, you can also just run the following mysql command.

mysql -e 'show tables' wordpress_database | grep options

prefix_options

Checking the Configured URL

From the command line, you can check what the current values of the home and siteurl lines in the options table. The command should send you and output like the example below. The important part is that these match each other under most circumstances and that they are what you expect. If they are not what you expect then you will want to update them accordingly.

mysql -e 'select * from wp_options where option_name rlike "home|siteurl"' wordpress_database
+-----------+-------------+----------------------------------+----------+
| option_id | option_name | option_value                     | autoload |
+-----------+-------------+----------------------------------+----------+
|        36 | home        | http://www.example.com           | yes |
|         1 | siteurl     | http://www.example.com           | yes |
+-----------+-------------+----------------------------------+----------+

Updating the Configured URL

The following command will update the two rows of the wp_options table for a given database to a new URL. This command should suit most situations where you need to update or correct the URL configured for a WordPress site. Updating the base_urls configured in a WordPress Multisite is beyond the scope of this article, but would involve updating multiple wp_option type tables.

mysql -e 'update wp_options set option_value="https://www.example.com" where option_name rlike "home|siteurl"' wordpress_database

Magento

The Magento database name is configured in one of the following files, local.xml or env.php. You can also configure a prefix for the table names of the Magento database, but it is usually not set. So the expected name for the main configuration table of the database is just core_config_data.

# Version 1.x
app/etc/local.xml

# Version 2.x
app/etc/env.php

The core_config_data Table

There are many potential URLs that can be configured, but they all have base_url as part of the line in the database. The primary URLs configured will be the secure and insecure URLs, but you can also setup URLs for images, theme files, or even set up a separate URL for the administration area of the site. You can again find these using a database management utility, but from the command line, you can run something like the following.

mysql -e 'select * from core_config_data where path rlike "base_url"' magento_database
+-----------+---------+----------+-----------------------+----------------------------+
| config_id | scope   | scope_id | path             | value |
+-----------+---------+----------+-----------------------+----------------------------+
|         3 | default |        0 | web/unsecure/base_url | http://www.example.com     |
|         4 | default |        0 | web/secure/base_url   | http://www.example.com   |
+-----------+---------+----------+-----------------------+----------------------------+

To update the base_urls in the Magento database, you would run something like the command below. Updating the base_urls of a Magento Multisite is also beyond the scope of this article, but would involve additionally referencing the specific scope_id value for the given website or store configured in the Magento database.

mysql -e 'update core_config_data set value="https://www.example.com" where path rlike "web/.*/base_url"' magento_database

Wrapping it All Up

With URLs configured in the database, as shown above, it is worth noting that these CMSs also provide their own redirect methods within the site code. If for example, you have a .htaccess redirect that is redirecting to a URL that does not align with what is in the database, you can end up with the infinite redirect loop as described earlier. However, you now know what some common .htaccess redirects look like, and where to find the configured URLs in the database of some CMS software. You are also well equipped to test, investigate, and confirm if these things are working in concert or if they are working against one another, and some steps to resolve them.

 

Author Bio

About the Author: Mark Cunningham

Mark currently works as an Enterprise System Administrator, whose long-term goal is to actually turn his job into a series of tiny shell scripts. He also enjoys making things outside of cyberspace. You might find him woodworking, machining, or on a photography outing when not working on servers all day.

Get 25% off the first month on WooCommerce Hosting! Find out why 30,000 customers have chosen our Best-in-Class Performance & 24x7x365 Support