Skip to content

Instantly share code, notes, and snippets.

@iconifyit
Last active February 10, 2023 03:09
Show Gist options
  • Save iconifyit/c8d715d62b6e3960696ac1c9cbd231c5 to your computer and use it in GitHub Desktop.
Save iconifyit/c8d715d62b6e3960696ac1c9cbd231c5 to your computer and use it in GitHub Desktop.
A/B Testing with htaccess
# ############################### #
# A/B TESTING (START) #
# ############################### #
# (1) Check if our cookie is already set.
# If so, redirect to the previously-viewed page.
RewriteCond %{HTTP_COOKIE} ab_test_vers=([^;]+)
RewriteRule ^THE-PAGE-BEING-TESTED$ HTTP://YOUR-DOMAIN.COM/tracking/%1 [cookie=ab_test_vers_match:true:YOUR-DOMAIN.COM,L]
# (2) If no cookie is set (new visitor)
# AND the current time is on an even-numbered second
# Rewrite to /test-option-a AND set our cookie to `even`
RewriteCond %{HTTP_COOKIE} !ab_test_vers=([^;]+)
RewriteCond %{TIME_SEC} [02468]$
RewriteRule ^test-page$ /tracking/even [cookie=ab_test_vers:even:YOUR-DOMAIN.COM,L]
Redirect 302 /tracking/even HTTP://YOUR-DOMAIN.COM/a/test-page
# (3) If no cookie is set (new visitor)
# AND the current time is on an odd-numbered second
# Rewrite to /test-option-b AND set our cookie to `odd`
RewriteCond %{HTTP_COOKIE} !ab_test_vers=([^;]+)
RewriteCond %{TIME_SEC} [13579]$
RewriteRule ^test-page$ /tracking/odd [cookie=ab_test_vers:odd:YOUR-DOMAIN.COM,L]
Redirect 302 /tracking/odd HTTP://YOUR-DOMAIN.COM/b/test-page
# ############################### #
# A/B TESTING (END) #
# ############################### #
@birender
Copy link

birender commented Jul 17, 2019

Hi @iconifyit
I have created two page on html website but I am unable to setup A/B TESTING on htaccess
can you let me know where I do mistake
++++++++++++++++++++++++++++++++
HTACCESS CODE
++++++++++++++++++++++++++++++++

################################ #

A/B TESTING (START)

################################ #

(1) Check if our cookie is already set.

If so, redirect to the previously-viewed page.

RewriteCond %{HTTP_COOKIE} ab_test_vers=([^;]+)
RewriteRule (.*) https://sendeagle.com/testse/ [cookie=ab_test_vers:true:sendeagle.com,L]

(2) If no cookie is set (new visitor)

AND the current time is on an even-numbered second

Rewrite to /test-option-a AND set our cookie

RewriteCond %{HTTP_COOKIE} !ab_test_vers=([^;]+)
RewriteCond %{TIME_SEC} [02468]$
RewriteRule ^index.html$ /index.html [cookie=ab_test_vers:even:sendeagle.com,L]

(3) If no cookie is set (new visitor)

AND the current time is on an odd-numbered second

Rewrite to /test-option-a AND set our cookie

RewriteCond %{HTTP_COOKIE} !ab_test_vers=([^;]+)
RewriteCond %{TIME_SEC} [13579]$
RewriteRule ^clone-home.html$ /clone-home.html [cookie=ab_test_vers:odd:sendeagle.com,L]

(4) We can use the rewritten URL to redirect the vistor to the page

they should see. The redirect can be to your won domain OR to an

external domain.

Redirect 302 /index.html https://sendeagle.com/index.html
Redirect 302 /clone-home.html https://sendeagle.com/testse/clone-home.html

NOTE: You can track the test results using Google Analytics by tracking the

landing pages and referrers.

###############################

A/B TESTING (END)

###############################

@iconifyit
Copy link
Author

iconifyit commented Jul 17, 2019

@birender I have updated the rules in the gist to be more simple. A few things:

  1. I have not tested this for entire sites. It is only meant to test a single page. I'm not an expert on A/B testing and I don't know what the best practices are for testing an entire site. That seems like a whole lot of variables to try to test at once. A single page is more manageable in my opinion but perhaps someone else (or you) knows better about this. I can modify it to work with an entire site but not for free. Sorry but this is how I make a living.
  2. In the first step you are trying to match ^(.*)$ but I think there is some misunderstanding. The variable %1 that is referenced in /tracking%1 is not the page name it is the even or odd being captured in the first RewriteCond, i.e., %{HTTP_COOKIE} ab_test_vers=([^;]+). The ([^;]+) is capturing the variable.
  3. In this rule RewriteRule (.*) https://sendeagle.com/testse/ [cookie=ab_test_vers:true:sendeagle.com,L] don't change ab_test_match to ab_test_vers. It is not a mistake. What you did changes the value of the variable and so breaks the cookie. ab_test_match just indicates that we found a match. The ab_test_match value is never used though. It is really just for debugging the rule if needed.
  4. You can't do this Redirect 302 /index.html https://sendeagle.com/index.html - it creates an endless loop.

I "think" all you need to do is:

  1. Replace all instances of YOUR-DOMAIN.COM with sendeagle.com (make sure to use https where indicated by http in the rules)
  2. Replace test-page with the name of the page being tested.
  3. Where you see HTTP://YOUR-DOMAIN.COM/a/test-page change this to https://sendeagle.com/test-page-option-a
  4. Where you see HTTP://YOUR-DOMAIN.COM/b/test-pagechange this to https://sendeagle.com/test-page-option-b
  5. Do not change the instances where the rules refer to tracking/odd, tracking/even, or /tracking/%1. These are used to sort the traffic into A or B requests.
  6. You will not be able to redirect to your index.html page. It will create an endless loop. So you will need to create a clone-home-a.html and clone-home-b.html.

I believe the rules below should work for you if you create the clone-home-a.html and clone-home-b.html.

RewriteCond %{HTTP_COOKIE} ab_test_vers=([^;]+)
RewriteRule ^$ /tracking/%1 [cookie=ab_test_vers_match:true:sendeagle.com,L]

RewriteCond %{HTTP_COOKIE} !ab_test_vers=([^;]+)
RewriteCond %{TIME_SEC} [02468]$
RewriteRule ^test-page$ /tracking/even [cookie=ab_test_vers:even:sendeagle.com,L]
Redirect 302 /tracking/even http://sendeagle.com/clone-home-a.html

RewriteCond %{HTTP_COOKIE} !ab_test_vers=([^;]+)
RewriteCond %{TIME_SEC} [13579]$
RewriteRule ^test-page$ /tracking/odd [cookie=ab_test_vers:odd:sendeagle.com,L]
Redirect 302 /tracking/odd http://sendeagle.com/clone-home-b.html

You can see a live demo of these rules at https://atomiclotusapparel.com/test-page

@birender
Copy link

birender commented Jul 17, 2019 via email

@iconifyit
Copy link
Author

@birender My pleasure. Let me know how it goes!

@birender
Copy link

birender commented Jul 17, 2019 via email

@iconifyit
Copy link
Author

Right on! That is fantastic news. Good luck!

@birender
Copy link

Hi,
I have another question:
If we have multiple pages for a/b split test then how can we equally distribute the request
for example :


we have 2 or more pages
Page 1
Page 2

visitor 1 we will show Product 1
visitor 2 we will show Product 2
visitor 3 we will show Product 1


@iconifyit
Copy link
Author

iconifyit commented Jul 19, 2019

I think the code below is what you need. The only caveat is that all of your test pages have to begin with "test-page", so "test-page-1", "test-page-2", etc. You can change this by replacing every instance of "test-page" with whatever prefix you want. Be sure to clear your cookies before testing the rules below.

# ############################### #
#        A/B TESTING (START)      #
# ############################### #

# (1) Check if our cookie is already set.
#     If so, redirect to the previously-viewed page.

RewriteCond %{HTTP_COOKIE} ab_test_vers=([^;]+)
RewriteRule ^(test-page.*)/?$ HTTPS://YOUR-DOMAIN-COM/tracking/%1/$1 [cookie=ab_test_vers_match:true:YOUR-DOMAIN-COM,L]

# (2) If no cookie is set (new visitor)
#     AND the current time is on an even-numbered second
#     Rewrite to /test-option-a AND set our cookie

RewriteCond   %{HTTP_COOKIE} !ab_test_vers=([^;]+)
RewriteCond   %{TIME_SEC} [02468]$
RewriteRule   ^(test-page.*/?)$ /tracking/even/$1 [cookie=ab_test_vers:even:YOUR-DOMAIN-COM,L]
RedirectMatch 302 ^/tracking/even/(.*/?)$ HTTPS://YOUR-DOMAIN-COM/a/$1

# (3) If no cookie is set (new visitor)
#     AND the current time is on an odd-numbered second
#     Rewrite to /test-option-a AND set our cookie

RewriteCond   %{HTTP_COOKIE} !ab_test_vers=([^;]+)
RewriteCond   %{TIME_SEC} [13579]$
RewriteRule   ^(test-page.*/?)$ /tracking/odd/$1 [cookie=ab_test_vers:odd:YOUR-DOMAIN-COM,L]
RedirectMatch 302 ^/tracking/odd/(.*/?)$ HTTPS://YOUR-DOMAIN-COM/b/$1

# ############################### #
#        A/B TESTING (END)        #
# ############################### #

@iconifyit
Copy link
Author

iconifyit commented Jul 19, 2019

@birender The rules below will probably be easier to manage. They test the entire site (if you want). You will need to create 3 sub-pages on your site: @, a, b so you'll end up with :

https://yourdomain.com/@/test-page-1
https://yourdomain.com/a/test-page-1
https://yourdomain.com/b/test-page-1

The flow will be that a user comes to https://yourdomain.com/@/some-page and be redirected to either https://yourdomain.com/a/some-page or https://yourdomain.com/b/some-page

So any link you use that starts with /@/any-page will be redirected to the same page name under /a/ or /b/ depending on the timestamp on their first visit.

# ############################### #
#        A/B TESTING (START)      #
# ############################### #

# (1) Check if our cookie is already set.
#     If so, redirect to the previously-viewed page.

RewriteCond %{HTTP_COOKIE} ab_test_vers=([^;]+)
RewriteRule ^@(.*)/?$ HTTPS://YOUR-DOMAIN-COM/tracking/%1/$1 [cookie=ab_test_vers_match:true:YOUR-DOMAIN-COM,L]

# (2) If no cookie is set (new visitor)
#     AND the current time is on an even-numbered second
#     Rewrite to /test-option-a AND set our cookie

RewriteCond   %{HTTP_COOKIE} !ab_test_vers=([^;]+)
RewriteCond   %{TIME_SEC} [02468]$
RewriteRule   ^@/(.*)/?$ /tracking/even/$1 [cookie=ab_test_vers:even:YOUR-DOMAIN-COM,L]
RedirectMatch 302 ^/tracking/even/(.*/?)$ HTTPS://YOUR-DOMAIN-COM/a/$1

# (3) If no cookie is set (new visitor)
#     AND the current time is on an odd-numbered second
#     Rewrite to /test-option-a AND set our cookie

RewriteCond   %{HTTP_COOKIE} !ab_test_vers=([^;]+)
RewriteCond   %{TIME_SEC} [13579]$
RewriteRule   ^@/(.*)/?$ /tracking/odd/$1 [cookie=ab_test_vers:odd:YOUR-DOMAIN-COM,L]
RedirectMatch 302 ^/tracking/odd/(.*/?)$ HTTPS://YOUR-DOMAIN-COM/b/$1

# ############################### #
#        A/B TESTING (END)        #
# ############################### #

@iconifyit
Copy link
Author

@birender I have created another gist with the whole-site version which you can find here : https://gist.github.com/iconifyit/1b5c51c142f2c929c27c953ae85a0f00

@ALPJeff
Copy link

ALPJeff commented Oct 16, 2019

Scott,

First, THANK YOU so much for sharing this, it's exactly what we were looking for & your very thorough explanation of everything was a huge help.

Next, I do have a question - do you have any insight on why this rule would cause JS issues on a website? We muddled through modifying the rule for homepage a/b testing like you see here & it wrecked some core eCommerce functions on our site, most likely my own fault:

# ############################### #
#        A/B TESTING (START)      #
# ############################### #

# (1) Check if our cookie is already set.
#     If so, redirect to the previously-viewed page.

RewriteCond %{HTTP_COOKIE} ab_test_vers=([^;]+)
RewriteRule ^$ /tracking/%1 [cookie=ab_test_vers_match:true:example.com,L]


# (2) If no cookie is set (new visitor)
#     AND the current time is on an even-numbered second
#     Rewrite to /test-option-a AND set our cookie

RewriteCond %{HTTP_COOKIE} !ab_test_vers=([^;]+)
RewriteCond %{TIME_SEC} [02468]$
RewriteRule ^$ /tracking/even [cookie=ab_test_vers:even:example.com,L]
Redirect 302 /tracking/even https://example.com/welcome/

# (3) If no cookie is set (new visitor)
#     AND the current time is on an odd-numbered second
#     Rewrite to /test-option-a AND set our cookie

RewriteCond %{HTTP_COOKIE} !ab_test_vers=([^;]+)
RewriteCond %{TIME_SEC} [13579]$
RewriteRule ^$ /tracking/odd [cookie=ab_test_vers:odd:example.com,L]
Redirect 302 /tracking/odd https://example.com/start-here/

# ############################### #
#        A/B TESTING (END)        #
# ############################### #

@iconifyit
Copy link
Author

Hey Jeff. That makes me happy to hear. I'm glad someone else finds it useful. I don't know off the top of my head why it would break your JS but it may have to do with the rewrite/redirect. I would be happy to take a quick look at the code if you want to email it to me at scott_at_atomiclotus_dot_net.

@birender
Copy link

birender commented Oct 18, 2019 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment