For years, there has been a subdomain verse subfolder debate.
One side says this, another side says that.
People have run their own tests, shown that moving a subdomain to a subfolder can improve a blogs ranking, yet so many have been in denial because Google has said the opposite.
Well, Aleyda Solis came out with direct test results, and a lot more people have finally jumped on the subfolder bandwagon.
Most of the time though, running a blog off a subfolder instead of a subdomain isn’t technically feasible.
It’s a pain to set up.
It was a shocking process the last time I tried to do it with a larger site 4/5 years ago.
However, lucky for us things have changed over the last couple of years.
If you use Cloudflare, you can now how you can have a blog installed on a subdomain, yet force it to load, and make it looks like it exists, under a subfolder.
Aleyda’s test
Kicking it off here, this is the tweet that stirred things up again.
The same blog previously in subdomain vs now in subdirectory comparison in SEMrush π pic.twitter.com/SIjh4tWQJx
— Aleyda Solis πΊπ¦ (@aleyda) July 15, 2022
Clearly highlighting the tech challenges that she’s gone through to get it going, Aleyda shows there’s a definitive growth following the migration to a subfolder.
An almost instant improvement.
There are plenty of caveats that could exist here, as it’s SEO after all…. but once the migration was complete you can see a nice upwards trend.
In some projects like this, I have seen what I dub a ‘new shiny’ effect. New URLs getting a little boost when discovered/migrated too, and then sometimes a bit of a drop off afterwards.
I reached out for an update to see if there was such a dip, and Aleyda was kind enough to provide a new graph;
Didn't dip ;) pic.twitter.com/ke2kjiMbOP
— Aleyda Solis πΊπ¦ (@aleyda) June 5, 2023
No post-launch dip! Nice.
Reverse proxies
Before we get into the different methods of setting this up, you need to understand reverse proxies.
Well, the super basics of them anyway…. which is where my knowledge of them stops anyway lol.
A reverse proxy allows you to essentially mask your website’s true file location, and load it somewhere else.
CloudFlare, in general, acts as a reverse proxy by being a CDN. It masks your server’s true location, by forwarding the requests from a Cloudflare ‘middle-man’ to it.
But this runs as URL in, URL out, where the URL is the same before and after the request and only the IP of the content is modified.
We can tweak and override this a bit, so that the URL of the request, differs from the URL of the content location.
So you could have a blog installed on the subdomain, and leave it there, but make it act and look like it’s actually installed on a subfolder.
A request will come through to https://domain.com/blog/ and then the reverse proxy will grab the content available at https://blog.domain.com, and then load it as if it actually exists at https://domain.com/blog/.
It allows you to bypass the biggest tech issue of running a blog on a subfolder, which is managing multiple different technologies installed in the same location.
Well, something along those lines anyway.
Subdomain to Subfolder with Nginx
Two previous blog moves from subdomain to subfolder that I have helped with, involved nginx reverse proxying.
Nginx is a server technology that does server things, but one thing it does is route traffic. You can give it filters or rules, and tell it to send specific requests one way, or another.
It’s like a little middleman that can move your site’s traffic around.
Using this, you tell it to reverse proxy your subfolder requests, to your subdomain, and have it look like everything loads under the subfolder.
Nginx has mostly been a more enterprise-level setup, so there’s a good chance you might not be using it.
If you have Nginx installed, here is a detailed guide on using it as a reverse proxy.
Subdomain to subfolder with Apache/.htaccesss
Similar to Nginx, Apache is another server tech that does similar things.
In particular, their htaccess file allows you to set this sort of thing up.
If you’re on a typical web hosting setup, this is the most likely setup you’ve got going.
To jump into a subfolder migration with the .htaccess, you can find a detailed guide here.
Subdomain to subfolder with Cloudflare
Now, this is my new favourite.
Why?
Because I can do it without any tech involvement, and it is independent of any other server tech.
And in under 2 minutes! Pretty sweet.
No messing around, it’s magical.
Many will also run Cloudflare before Nginx / Apache is hit, so it will work across both and be a bit more flexible.
Today, I will show you how you can do it too.
How to setup a reverse proxy with Cloudflare
It first started with a guide I found from 403.ie here.
There were a few others floating around, but this was the best one I could find to match the specific requirement of reverse proxying content from a subdomain to a subfolder leveraging Cloudflare.
However, it, unfortunately, didn’t work for me. It was close, but the WordPress side of things kept failing.
It took a few goes to work out whether it was the server (Siteground has some fun caching :/) or whether it was the Cloudflare setup.
Modifying the DB directly, some WordPress config scripts, and many other changes, but every time something else would break.
Missing CSS files, bad redirects, and constant server errors. Every time I patched something, something else would break.
I gave up, and called in some dev support.
A dev named Dat came through, and sorted me out.
Steps to setup the reverse proxy
The following instructions will help you get your reverse proxy setup in both Cloudflare, and your WordPress setup.
1. Create the Cloudflare workers
- Log into your Cloudflare account, but don’t load up any of the sites, and you’ll see the ‘Workers & Pages’ setting option;
- Jump in here, and click ‘create application’
- Then find ‘create worker’
- Name the first one;
sitename-reverse-proxy
Modifying the sitename to be your actual site name. This name can be anything, but including the sitename can help incase you want to do this multiple times, as each worker could be loaded under any domain.
- Click deploy, and it will load in a default script, which we will replace.
- Click on ‘edit code’;
- Delete the default code, and then paste in the first set of code from below, for the ‘reverse proxy worker’.
- Modify any mention of blog.domain.com or domain.com/blog to be the settings you require.
Be careful not to modify any existing, or add any new, trailing slashes or https mentions as they will break everything.
- Click on ‘Save and deploy’ in the top right corner, and then ‘Save and deploy’ again on the little popup modal
- Repeat the above steps for the redirect worker, named “sitename-redirect-worker”, and get that one deployed too.
2. Setup the Cloudflare routes
- Open the website you wish to add the routes for, and then find ‘Workers Routes’
- Click on ‘add route’
- Create routes for the following two URLs (modifying them to match what you need), by selecting the redirect service worker you created, and ‘production’ environment
*blog.domain.com
*blog.example.com/*
- Create routes for the following two URLs (modifying them to match what you need), by selecting the proxy service worker you created, and ‘production’ environment
*domain.com/blog
*domain.com/blog*
- After both sets of 2 routes have been created, you will see something similar to this on your Workers Routes page;
3. Modify the WordPress Site URL
The easiest step of them all.
Load up the WordPress admin area, and jump into Settings > General.
Modify the Site Address, and not the WordPress address, as per the below settings;
4. Add a trailing slash redirect for the sub-folder
After all this, we couldn’t get a final issue solved in the end, unfortunately. The blog homepage was available with both the trialing slash, and no trailing slash. Just the homepage. Everything else works beautfulllllly.
- To get this patched up, we load up Cloudflare and head to the Rules > Redirect Rules
- Click ‘Create rule’ under the single redirects section
- Create a rule that uses the non-trailing slash blog URL version as the incoming requests rule, and the same URL but with a trailing slash as the URL to redirect these requests to
5. Implement a redirect strategy
If this is an existing build you’re modifying, make sure you implement a full 301 redirect strategy! It should just be a simple 301 rule that forwards from the sub-domain to a sub-folder, but triple check it all.
There’s no point moving to a sub-folder if you break everything along the way.
The scripts
Reverse proxy worker
addEventListener('fetch', event => { // Skip redirects for WordPress preview posts if (event.request.url.includes('&preview=true')) { return; } event.respondWith(handleRequest(event.request)) }) class AttributeRewriter { constructor(rewriteParams) { this.attributeName = rewriteParams.attributeName this.old_url = rewriteParams.old_url this.new_url = rewriteParams.new_url } element(element) { const attribute = element.getAttribute(this.attributeName) if (attribute && attribute.startsWith(this.old_url)) { element.setAttribute( this.attributeName, attribute.replace(this.old_url, this.new_url), ) } } } const rules = [ { from: 'domain.com/blog', to: 'blog.domain.com' }, // more rules here ] const handleRequest = async req => { // Redirect WordPress login to the subdomain let baseUrl = req.url; if (baseUrl.includes('domain.com/blog/wp-login.php')) { return new Response('', { status: 302, headers: { 'Location': baseUrl.replace('domain.com/blog', 'blog.domain.com') } }); } const url = new URL(req.url); let fullurl = url.host + url.pathname; var newurl = req.url; var active_rule = { from: '', to: '' } rules.map(rule => { if (fullurl.startsWith(rule.from)) { let url = req.url; newurl = url.replace(rule.from, rule.to); active_rule = rule; console.log(rule); } }) const newRequest = new Request(newurl, new Request(req)); const res = await fetch(newRequest); const rewriter = new HTMLRewriter() .on('a', new AttributeRewriter({ attributeName: 'href', old_url: active_rule.from, new_url: active_rule.to })) .on('img', new AttributeRewriter({ attributeName: 'src', old_url: active_rule.from, new_url: active_rule.to })) .on('link', new AttributeRewriter({ attributeName: 'href', old_url: active_rule.from, new_url: active_rule.to })) .on('script', new AttributeRewriter({ attributeName: 'src', old_url: active_rule.from, new_url: active_rule.to })) // .on('*', new AttributeRewriter({ attributeName: 'anytext', old_url: active_rule.from, new_url: active_rule.to })) if (newurl.indexOf('.js') !== -1 || newurl.indexOf('.xml') !== -1) { return res; } else { return rewriter.transform(res); } }
Redirect worker
const base = "https://domain.com/blog" const statusCode = 301 async function handleRequest(request) { const excludedPaths = ['/wp-login.php', '/wp-admin', '/wp-admin/'] const url = new URL(request.url) const { pathname, search, hash } = url const destinationURL = base + pathname + search + hash if (excludedPaths.some(path => pathname.startsWith(path))) { return fetch(request) } else { return Response.redirect(destinationURL, statusCode) } } addEventListener("fetch", async event => { event.respondWith(handleRequest(event.request)) })
Your subdomain to subfolder setup should be live
Following the steps above, your blog should now be publicly accessible under the sub-folder, and the admin panel will be accessible under the original sub-domain.
You should be able to directly load the new subfolder, and it’ll work as if you’re accessing the subdomain where it is actually installed.
I’d love to hear how it goes if you went ahead with the change! Both whether the instruction above completely worked for you, and how performance was if it was an existing build you modified.
4 Replies to “Subdomain to Subfolder: The Simple Cloudflare Reverse Proxy”
Hi, Sam
First of all, thank you so much for sharing this amazing method.
I’ve been searching for this method for a long time, and I think I finally found the perfect guide.
But unfortunately, I am getting a 404 error. I have tried several methods to solve the problem, but nothing worked, so I would like to ask a little more.
I’m using Nginx, and on my real server, the website has the path below, with each server block. and origin website not proxied from Cloudflare.
1) Does the origin site (domain.com in the article) also need to proxied from Cloudflare?
2) Do I actually need to have the WordPress files for the subdomain in a subfolder, like this?
ex) /var/www/example.com/blog.example.com
3) Is it correct that in cloudflare, the DNS of the subdomain is pointed to A? Should I specify the origin site as the CNAME?
4) do i need to add something to the Server block like Location~?
I look forward to your advice, thanks.
Hi, I asked about the 404 error last time, but i’ve resolved that issue.
Now the only issue left for me is CORS – Redirect error on the admin page of example.com/blog.
In the console, I see the following error
Access to fetch at ‘https://blog.example.com/index.php?rest_route=%2Fwp%2Fv2%2Ftaxonomies%2Fpost_tag&context=edit&_locale=user’ from origin ‘https://example.com’ has been blocked by CORS policy: Response to preflight request doesn’t pass access control check: Redirect is not allowed for a preflight request.
I can’t solve this problem no matter how hard I try.
Could you please advise if there is a workaround for this issue? Thank you very much.
Hey there!
Glad you resolved the 404 issues.
For the admin, you access the admin under the subdomain where wp is installed, and sounds like you’re accessing under the subfolfer. The instructions and code ignore the admin section under the subdomain, so try access it at blog.domain.com/wp-admin/.
It should be the only part that doesn’t redirect you.
Cheers,
Sam.
Hello there, great post!
Any idea why Yoast can’t scan the content?
I got the next error.
Thank you in advance.-
Oops, something has gone wrong and we couldn’t complete the optimization of your SEO data. Please click the button again to re-start the process.
Below are the technical details for the error. See this page for a more detailed explanation.
Error details
Request URL
https://domain.tld/blog/wp-json/yoast/v1/indexing/prepare
Request method
POST
Status code
403
Error message
Cookie check failed