Originally posted by mikegrogan June 9, 2026
Hi. Our local environment uses Commandbox using a single site architecture. On our production server it's ACF with IIS in front of it. We have 6 or so separate Wheels instances running under subfolders.
Directory Structure
├── config/
│ ├── coldfusion/
│ │ └── cfconfig.json
│ └── urlrewriting/
│ └── rules.txt
├── server.json <--- everything setup for commandbox
└── wwwroot/
├── wheelsproject1/
├── wheelsproject2/
├── wheelsproject3/
└── wheelsproject4/
├── app/
├── config/
│ ├── settings.cfm
├── public/
├── tests/
│ ├── runner.cfm
│ └── TestRunner.cfc
└── box.json
Wheels obviously wasn't completely intended to be used under a subfolder, but it's worked well for years. In trying to move to wheels 3 and then 4 quickly i had to rewrite the URL rules.
Commandbox URL rewriting in wheels 4 (rules.txt) (when setup as a subfolder)
# 1. Skip standard ColdFusion/Lucee engine paths completely
regex( pattern='^/(wheelsproject1|wheelsproject2|wheelsproject3)/(flex2gateway|jrunscripts|cfide|cf_scripts|cfformgateway|cffileservlet|lucee)', case-sensitive=false ) -> done;
# 2. MATCH PHYSICAL ASSETS: Route asset folders straight into the /public folder and stop processing cleanly
regex( pattern='^/(wheelsproject1|wheelsproject2|wheelsproject3)/(?:public/)?(files|images|javascripts|miscellaneous|stylesheets|robots.txt|favicon.ico|sitemap.xml)(.*)$', case-sensitive=false ) -> { rewrite('/${1}/public/${2}${3}'); done; }
# 3a. ALLOW FORM POSTS WITH /PUBLIC/: Rewrite silently so POST payload data isn't lost.
method(POST) and regex( pattern='^/(wheelsproject1|wheelsproject2|wheelsproject3)/public/(?!index\.cfm)(.*)$', case-sensitive=false ) -> { rewrite('/${1}/public/index.cfm/${2}%q'); done; }
# 3b. STRIP PUBLIC FROM URL BAR (GET requests only): Hard-redirect to the clean path and PRESERVE THE QUERY STRING
method(GET) and regex( pattern='^/(wheelsproject1|wheelsproject2|wheelsproject3)/public/(?!index\.cfm|files|images|javascripts|miscellaneous|stylesheets|robots.txt|favicon.ico|sitemap.xml)(.*)$', case-sensitive=false ) -> redirect('/${1}/${2}%q');
# 4. STANDARD ROUTING: Map clean MVC URLs invisibly to the nested index.cfm front controller (Excluding tests)
regex( pattern='^/(wheelsproject1|wheelsproject2|wheelsproject3)/(?!public/|tests/)(.*)$', case-sensitive=false ) -> rewrite('/${1}/public/index.cfm/${2}%q');
For IIS in wheels4 I'm setting up web.config files under each application.
config/settings.cfm Tweak to hide public in urls
if (StructKeyExists(application, "$wheels") AND StructKeyExists(application.$wheels, "webPath")) {
if (Right(application.$wheels.webPath, 8) EQ "/public/") {
application.$wheels.webPath = ReplaceNoCase(application.$wheels.webPath, "public/", "");
// Keep rootPath and rootcomponentPath synchronized with the clean webPath
application.$wheels.rootPath = "/" & ListChangeDelims(application.$wheels.webPath, "/", "/");
application.$wheels.rootcomponentPath = ListChangeDelims(application.$wheels.webPath, ".", "/");
application.$wheels.wheelsComponentPath = ListAppend(application.$wheels.rootcomponentPath, "wheels", ".");
}
}
Everything just loads up like: https://hostname:443/wheelsproject1, https://hostname:443/wheelsproject2, https://hostname:443/wheelsproject3, etc.
Again, this has worked fine for years under 2.5. However the wheels-core pages and testing always kind of break slightly. Navigation links don't work as they are hard coded. Minor things that I can tweak.
But now with the new vendor architecture and the possibility of overwriting any core tweaks with an update using Box install I'm wondering if any consideration could be made to allow wheels to run under a sub folder? To be clear, the only thing really not working from what I can see is the tests folder. Have the code not assume it's always running under the root?
Tests Review so far (running under a subfolder)
I'm not really at the point to understand it enough to create a PR, but I did finally get it working in a hacked sort of way through the web interface. The CLI is still broken, but haven't reviewed.
Using the wheels bottom nav I can finally load up https://localhost/wheelsproject1/wheels/app/tests and have a successful page load and tests passing. It's not perfect though. The application scope hacks break the main application, which a reload=true fixes. They seem to fight each other and haven't figured it out.
- Part of the issue is under
public/application.cfc. It sets the this.wheels.rootPath = getDirectoryFromPath(getBaseTemplatePath());. I had to tweak it to something like
local.currentDirectory = getDirectoryFromPath(getCurrentTemplatePath());
this.wheels.rootPath = getCanonicalPath(local.currentDirectory & "../");
- The application setup still wasn't getting into the /tests folder properly. I was trying to setup a /tests/application.cfc and inherit some of the information from public/application.cfc. Again it kind of works, but is too hacky to really share.
- Worked through some Oracle database syntax issues, one that I did a PR.
That's about as far as I got. I'm trying to run a test wheels4 app under the root and compare, but again not perfect yet. Thanks for any review of running wheels as as subfolder!
Discussed in #2887
Originally posted by mikegrogan June 9, 2026
Hi. Our local environment uses Commandbox using a single site architecture. On our production server it's ACF with IIS in front of it. We have 6 or so separate Wheels instances running under subfolders.
Directory Structure
Wheels obviously wasn't completely intended to be used under a subfolder, but it's worked well for years. In trying to move to wheels 3 and then 4 quickly i had to rewrite the URL rules.
Commandbox URL rewriting in wheels 4 (rules.txt) (when setup as a subfolder)
For IIS in wheels4 I'm setting up web.config files under each application.
config/settings.cfm Tweak to hide public in urls
Everything just loads up like: https://hostname:443/wheelsproject1, https://hostname:443/wheelsproject2, https://hostname:443/wheelsproject3, etc.
Again, this has worked fine for years under 2.5. However the wheels-core pages and testing always kind of break slightly. Navigation links don't work as they are hard coded. Minor things that I can tweak.
But now with the new vendor architecture and the possibility of overwriting any core tweaks with an update using Box install I'm wondering if any consideration could be made to allow wheels to run under a sub folder? To be clear, the only thing really not working from what I can see is the
testsfolder. Have the code not assume it's always running under the root?Tests Review so far (running under a subfolder)
I'm not really at the point to understand it enough to create a PR, but I did finally get it working in a hacked sort of way through the web interface. The CLI is still broken, but haven't reviewed.
Using the wheels bottom nav I can finally load up https://localhost/wheelsproject1/wheels/app/tests and have a successful page load and tests passing. It's not perfect though. The application scope hacks break the main application, which a reload=true fixes. They seem to fight each other and haven't figured it out.
public/application.cfc. It sets the this.wheels.rootPath = getDirectoryFromPath(getBaseTemplatePath());. I had to tweak it to something likelocal.currentDirectory = getDirectoryFromPath(getCurrentTemplatePath());
this.wheels.rootPath = getCanonicalPath(local.currentDirectory & "../");
That's about as far as I got. I'm trying to run a test wheels4 app under the root and compare, but again not perfect yet. Thanks for any review of running wheels as as subfolder!