Variable Scopes
They gotta exist somewhere!
In the BoxLang language, many persistence and visibility scopes exist for variables to be placed in. These are differentiated by context: in a class, function, component, thread, script or template. These scopes are Java maps but enhanced to provide case-insensitivity, member functions and more. There are also different scopes that become alive depending on the execution of certain constructs, such as queries, threads, and http calls.
When you declare variables in templates, classes, or functions, they’re stored within a structured scope. This design makes your code highly flexible, allowing you to fluently interact with the entire scope through a wide range of BIFs. You can even bind these variables directly to function calls, attributes, and more—highlighting the dynamic power at the core of BoxLang.
// Examples
/**
* Get the state representation of the scheduler task
*/
function getMemento(){
// I can do a filter on an entire scope
return variables.filter( ( key, value ) => {
return isCustomFunction( value ) || listFindNoCase( "this", key ) ? false : true;
} );
}
// Argument binding
results = matchClassRules( argumentCollection = arguments );
results = matchClassRules( argumentCollection = myMap );
// I can dump entire scopes
writedump( variables )
The only language keyword that can be used to tell the variable to store in a function's local scope is called var
. However, you can also just use the local
scope directly or none at all. However, we do recommend being explicit on some occasions to avoid ambiguity.
It's a good idea to scope variables to avoid scope lookups, which could in turn create issues or even leaks.
function getData(){
// Placed in the function's local scope
var data = "luis"
// Also placed in the function's local scope
data = "luis"
// Also placed in the function's local scope
local.data = "luis"
}
📄 Template/Scripts (bxm,bxs
)
bxm,bxs
)All scripts and templates have the following scopes available to them. Please note that the variables
scope can also be implicit. You don't have to declare it.
variables
- The default or implicit scope to which all variables are assigned.
a = "hello"
writeOutput( a )
// Is the same as
variables.a = "hello"
writeOutput( variables.a )
⚡Templates/Scripts Function Scopes
All user-defined functions declared in templates or scripts will have the following scopes available:
variables
- Has access to private variables within a Class or Pagelocal
- Function-scoped variables only exist within the function execution. Referred to asvar
scoping. The default assignment scope in a function.arguments
- Incoming variables to a function
function sayHello( required name ){
var fullName = "Hello #arguments.name#"
// case insensitive
return FULLNAME
}
writeOutput( sayHello( "luis" ) )
🗳️ Classes (bx
)
bx
)All classes in BoxLang follow Object-oriented patterns and have the following scopes available. Another critical aspect of classes is that all declared functions will be placed in a visibility scope.
variables
- Private scope, visible internally to the class onlythis
- Public scope, visible from the outside world and a self-reference internallystatic
- Store variables in the classes blueprint and not the instancesuper
- Only available if you use inheritance
class extends="MyParent"{
static {
className = "MyClass"
}
variables.created = now()
this.PUBLIC = "Hola"
function init(){
super.init()
}
function main(){
println( "hello #static.className#" )
}
}
Function Scopes
Depending on a function’s visibility, BoxLang automatically places a reference (or pointer) to that function in different scopes, within the class ensuring that it can be discovered and invoked appropriately. Why does it do this? Because BoxLang is a dynamic language by design.
In practice, this means that functions in BoxLang are not locked down at compile time. Instead, they remain fully malleable at runtime—you can add new functions, remove existing ones, or even modify their behavior on the fly. This flexibility allows developers to build more adaptive and expressive applications, where behaviors can evolve based on context, configuration, or user interaction.
By managing function pointers across scopes, BoxLang ensures that these runtime changes are immediately reflected wherever the function is expected to live. Whether it’s within a local scope, a component, or a broader application context, the dynamic nature of BoxLang makes it possible to treat functions as living members of your application rather than static, unchangeable artifacts.
Private
Functionvariables
Public or Remote
Functionsvariables
this
// Try it out
class {
function main(){
writedump( var: this, label: "public" );
writedump( var: variables, label: "private" );
}
private function test(){}
public function hello(){}
}
⚡Closures
All closures in BoxLang are context-aware, which means they carry with them knowledge of the environment in which they were created. In other words, a closure isn’t just a function—it’s a function plus the scope it was born in. This allows closures to maintain access to variables, functions, and references that existed at the time they were defined, even if they are later executed in a completely different context.
For example, if you define a closure inside a function, it will continue to “remember” the local variables of that function, making it a powerful tool for encapsulating state and creating reusable logic without polluting the global or class-level scopes.
When closures are defined inside classes, they become even more useful. In this case, closures automatically capture the class’s variables
scope (the private, per-instance data) as well as the this
scope (the public interface of the instance). This means closures have seamless access to both the internal state of the class and its public methods, enabling elegant encapsulation of behavior that can directly interact with the object’s data.
This context-awareness is what makes closures in BoxLang so flexible. They’re not isolated pieces of code; they are “living” functions that carry their birthplace with them. This ensures that wherever you pass them—whether into a higher-order function, as an event handler, or across asynchronous execution—they still know exactly where they came from and what they can interact with.
variables
- Has access to private variables from where they were created (Classes, scripts or templates)this
- Has access to public variables from where they were created (Classes)local
- Function-scoped variables only exist within the function execution. Referred to asvar
scoping. The default assignment scope in a function.arguments
- Incoming variables to a function
variables.CONSTANT = 1.4
// Define a closure that returns another function
function makeMultiplier( factor ) {
return ( number ) => number * factor * variables.CONSTANT;
}
// Create multiplier functions using closures
double = makeMultiplier( 2 )
triple = makeMultiplier( 3 )
// Call the closure functions
println( double( 5 ) ) // Output: 10
println( triple( 5 ) ) // Output: 15
// Closures can also capture outer variables
function counter() {
var count = 0;
return () => {
count++
return count
};
}
increment = counter()
println( increment() ) // Output: 1
println( increment() ) // Output: 2
println( increment() ) // Output: 3
🪶 Lambdas
Lambdas in BoxLang are designed to be pure functions. Unlike closures, they do not capture or carry along the scope in which they were defined. In other words, lambdas don’t “remember” their birthplace or have access to surrounding variables. They exist independently of the context in which they were created.
This distinction has two major implications:
Simplicity – Because lambdas are context-free, they are lightweight and predictable. They only operate on the
arguments
explicitly passed to them and any values they define internally. You don’t have to worry about hidden dependencies or external scope leaks. This makes them ideal for short, focused operations like transformations, mappings, or small inline computations.Performance – Without the overhead of capturing surrounding scopes, lambdas are faster to create and execute compared to closures. They don’t need to carry around extra baggage such as references to variables, this, or parent function contexts. This efficiency makes them an excellent choice in performance-sensitive scenarios, especially when used in large iterations, functional pipelines, or hot paths in your application.
In essence, lambdas in BoxLang provide a lean, functional style of programming: self-contained, efficient, and free of side effects. They shine when you need a quick function without any ties to the broader context—perfect for concise logic that should remain pure and context-independent.
local
- Function-scoped variables only exist within the function execution. Referred to asvar
scoping. The default assignment scope in a function.arguments
- Incoming variables to a function
// Define a simple lambda that squares a number
square = (x) -> x * x
println( square( 4 ) ) // Output: 16
println( square( 5 ) ) // Output: 25
// Lambda that adds two numbers
add = (a, b) -> a + b
println( add( 10, 20 ) ) // Output: 30
// Using a lambda to filter even numbers
numbers = [1, 2, 3, 4, 5, 6]
evens = numbers.filter( (n) -> n % 2 == 0 )
println( evens ) // Output: [2, 4, 6]
// Sorting a list in descending order
numbers = [3, 1, 4, 1, 5, 9]
numbers.sort( (a, b) -> b - a )
println( numbers ) // Output: [9, 5, 4, 3, 1, 1]
The following will throw an exception as it will try to reach outside of itself:
function testLambdaScope() {
localVar = "I am local"
// This lambda will fail because it tries to access localVar
brokenLambda = () -> localVar
println( brokenLambda() ) // This will cause an error
}
testLambdaScope()
🏷️ Components
In BoxLang, you’re not limited to the built-in components—you can extend the language itself by creating your own custom components. These components can be invoked seamlessly in both script syntax and templating syntax, giving developers a consistent way to encapsulate and reuse functionality across different coding styles.
Custom components are powerful because they allow you to wrap up behavior into a self-contained unit that can handle input, manage its own internal state, and even communicate back to the calling template. This makes them excellent for building reusable UI constructs, workflow blocks, or domain-specific abstractions. They also do not affect the parser, but rather are invoked at runtime. This means, that you can create predictable, reusable components without worrying about breaking the syntax of your BoxLang code.
Every custom/core component automatically has access to a set of special scopes that give it flexibility and power:
• attributes – Holds the incoming arguments or attributes passed into the component. This is how you provide input to customize the component’s behavior. • variables – Acts as the default scope for variable assignments inside the component, ensuring that the component has its own sandboxed data environment. • caller – Provides a way for the component to interact with the template that invoked it, allowing you to set or read variables from the caller’s context. This enables two-way communication between the component and the outside world.
Example Usage
You can call a component using script syntax, where it looks and feels like a function call with a block of content:
// Call a component using script
bx:component template="path/MyComponent.bxm" name="luis"{
// Content here or more component calls or whatever
}
Or you can invoke it using templating syntax, which is closer to traditional custom tags:
<!-- Templating syntax: call a component -->
<bx:component template="path/MyComponent.bxm" name="luis">
Inner content, text, or other nested components
</bx:component>
We highly discourage peeking out of your component using the caller
scope, but it can sometimes be beneficial. Use with caution.
🧵 Thread
When you create threads with the thread
component, you will have these scopes available to you:
attributes
- Passed variables via a threadthread
- A thread-specific scope that can be used for storage and retrieval. Only the owning thread can write to this scope. All other threads and the main thread can read the variables.local
- Variables local to the thread contextvariables
- The surrounding declared template or classvariables
scopethis
- The surrounding declared class scope
bx:thread
name="exampleThread"
message="Hello from the thread!"
{
// You can use var, local or none
var incomingMessage = attributes.message
// Defining local variables inside the thread
local.threadLocalVar = "This is a local thread variable"
threadLocalVar2 = "Another local var"
// Setting variables in the thread scope (can be accessed after completion)
// Only this thread can write to it.
thread.finalMessage = "Thread has completed execution!"
thread.calculationResult = 42
println( "Thread is running..." )
println( "Incoming Message: " & incomingMessage )
println( "Local Variable in Thread: " & threadLocalVar )
}
// Wait for the thread to finish before accessing thread scope variables
threadJoin( "exampleThread" )
// Accessing thread scope variables after execution
println( "Thread Final Message: " & bxthread.exampleThread.finalMessage )
println( "Thread Calculation Result: " & exampleThread.calculationResult )
🧵 bxThread
The bxThread
scope is a special BoxLang scope that provides access to thread metadata and variables from anywhere in your request. It contains a key for every thread that has been executed in the current request, making it easy to access thread data from outside the thread context.
bxThread
- Global scope containing all threads executed in the current requestEach thread key contains metadata and variables set from within that thread
// Creating multiple threads
bx:thread name="dataProcessor" id="123" {
thread.processedData = "Data processed for ID: #attributes.id#"
thread.timestamp = now()
}
bx:thread name="emailSender" recipient="[email protected]" {
thread.emailSent = true
thread.recipient = attributes.recipient
thread.sentAt = now()
}
// Wait for threads to complete
threadJoin( "dataProcessor,emailSender" )
// Access thread data from anywhere using bxThread scope
println( "Data Processor Result: " & bxThread.dataProcessor.processedData )
println( "Email Status: " & bxThread.emailSender.emailSent )
println( "Email Recipient: " & bxThread.emailSender.recipient )
// You can also iterate over all threads in the current request
for( threadName in bxThread ) {
println( "Thread: #threadName# completed at #bxThread[threadName].timestamp#" )
}
🌐 Runtime Scopes
BoxLang runs in different runtime contexts, and the available scopes depend on which runtime you're using. Understanding these differences is crucial for building portable applications.
🖥️ Core OS
When running BoxLang in CLI mode or core OS runtime, you have access to these scopes:
variables
- Default scope for variable assignmentslocal
- Function-scoped variablesarguments
- Function argumentsapplication
- Application-wide persistence (requires Application.bx)session
- Session persistence (requires Application.bx)request
- Request-scoped variablesserver
- Server-wide persistence across all applicationsthread
- Thread-specific variablesbxThread
- Global thread metadata scope
// CLI Example - Only core scopes available
server.appName = "My CLI App"
request.startTime = now()
application.version = "1.0.0" // Requires Application.bx
println( "Running in CLI mode with core scopes only" )
🕸️ Web Runtimes
When running in web runtimes (Servlet, MiniServer, Lambda, Desktop), you get additional web-specific scopes:
All Core Scopes PLUS:
url
- HTTP GET parameters from the URLform
- HTTP POST form datacgi
- Server and request environment variablescookie
- Browser cookiesbxFile
- File upload information (during file uploads)bxHttp
- HTTP request/response data (during HTTP operations)
// Web Example - Full scope access
url.debug = "true" // From: myapp.com?debug=true
form.username = "john.doe" // From POST form
cookie.preferences = "dark-mode" // Browser cookie
cgi.remote_addr = "192.168.1.1" // Client IP address
println( "Running in web mode with full scope access" )
println( "Client IP: #cgi.remote_addr#" )
println( "Debug Mode: #url.debug#" )
🔍 Unscoped Variables
If you use a variable name without a scope prefix, BoxLang checks the scopes in the following order to find the variable (When we say function
it includes closures, UDFs and lambdas):
Local (Functions only)
Arguments (Functions only)
Attributes (Components only)
Thread local (inside threads only)
Query (variables in active query loops)
Thread
Variables
If the runtime is web-based, it will also check these scopes last:
CGI
URL
Form
Cookie
IMPORTANT: Because BoxLang must search for variables when you do not specify the scope, you can improve performance by specifying the scope for all variables. It can also help you avoid nasty lookups or unexpected results.
🔒 Final Variables
BoxLang supports the final
modifier for variables in any scope, which prevents the variable from being reassigned after its initial assignment. This is useful for creating constants and preventing accidental modifications.
// Class scope with final variables
class {
final variables.API_VERSION = "1.0"
final this.MAX_RETRIES = 3
final static.COMPANY_NAME = "Ortus Solutions"
function init() {
final local.instanceId = createUUID()
// This would throw an error:
// API_VERSION = "2.0" // Cannot reassign final variable
return this
}
}
// Function scope with final variables
function processData( required data ) {
final var startTime = now()
final local.processId = createUUID()
// Process data here...
// These would throw errors:
// startTime = now() // Cannot reassign final variable
// processId = "new-id" // Cannot reassign final variable
return {
"processId" : processId,
"duration" : dateDiff( "s", startTime, now() )
}
}
// Persistence scopes with final variables
final application.appVersion = "1.0.0"
final session.userType = "premium"
final request.requestId = createUUID()
Important: Final variables must be assigned a value when declared. Attempting to reassign a final variable will result in a runtime error.
💾 Persistence Scopes
Can be used in any context, used for persisting variables for a period of time.
session
- stored in server RAM or external storages tracked by unique web visitorapplication
- stored in server RAM or external storage tracked by the running BoxLang applicationcookie
- stored in a visitor's browserserver
- stored in server RAM for ANY application for that BoxLang instancerequest
- stored in RAM for a specific user request ONLYcgi
- read only scope provided by the servlet container and BoxLangform
- Variables submitted via HTTP postsURL
- Variables incoming via HTTP GET operations or the incoming URL
Practical Persistence Examples
// 🔧 Server Scope - Cross-application persistence
server.startupTime = now()
server.totalRequests = ( server.totalRequests ?: 0 ) + 1
server.sharedCache = {}
// 📱 Application Scope - Application-wide data
application.version = "2.1.0"
application.userCount = 0
application.settings = {
"debugMode" : false,
"maxUsers" : 1000
}
// 👤 Session Scope - User-specific data (Web only)
session.userId = "user123"
session.preferences = {
"theme" : "dark",
"language" : "en"
}
session.shoppingCart = []
// 🍪 Cookie Scope - Browser-stored data (Web only)
cookie.lastVisit = now()
cookie.userPrefs = "theme=dark;lang=en"
// Set cookie with options
bx:cookie name="sessionToken" value="abc123" expires="30" secure="true"
// 📨 Request Scope - Single request data
request.startTime = now()
request.userAgent = cgi.http_user_agent
request.processingSteps = []
// 📊 CGI Scope - Read-only server/request info (Web only)
println( "Client IP: #cgi.remote_addr#" )
println( "Request Method: #cgi.request_method#" )
println( "User Agent: #cgi.http_user_agent#" )
println( "Server Name: #cgi.server_name#" )
// 📝 Form Scope - POST data (Web only)
if( structKeyExists( form, "username" ) ) {
println( "Username submitted: #form.username#" )
println( "Email: #form.email#" )
}
// 🔗 URL Scope - GET parameters (Web only)
if( structKeyExists( url, "action" ) ) {
switch( url.action ) {
case "login":
// Handle login
break
case "logout":
// Handle logout
break
}
}
Scope Interaction Examples
// Copy data between scopes
session.userPreferences = duplicate( form )
// Merge scopes
request.allData = {}
structAppend( request.allData, url )
structAppend( request.allData, form )
Scope Debugging & Performance
// Debugging scopes - dump entire scopes
writeDump( var=variables, label="Variables Scope" )
writeDump( var=session, label="Session Data", top=10 )
writeDump( var=application, label="App Settings" )
// Check scope contents
if( structIsEmpty( session ) ) {
println( "No session data found" )
}
// Performance - scope size monitoring
function getScopeInfo() {
return {
"variablesCount" : structCount( variables ),
"sessionSize" : structCount( session ),
"applicationKeys" : structKeyArray( application ),
"requestMemory" : structCount( request )
}
}
// Scope iteration for debugging
for( key in variables ) {
if( isCustomFunction( variables[key] ) ) {
println( "Function found: #key#" )
}
}
// Performance best practices
function efficientScopeUsage() {
// ✅ Good - explicit scoping
local.result = variables.data.process()
// ❌ Bad - unscoped variable (scope hunting)
result = data.process() // BoxLang must search scopes
// ✅ Good - cache scope references
local.mySession = session
local.mySession.lastAction = now()
local.mySession.pageViews++
// ❌ Bad - repeated scope access
session.lastAction = now()
session.pageViews++
}
🖥️ Server Scope
The server
scope is a global scope that lives for the entire lifetime of the BoxLang runtime instance. It contains system information, BoxLang runtime details, and can store custom variables that need to persist across all applications and requests. It contains the following sub-scopes:
boxlang
- BoxLang runtime informationcli
- Command line execution detailsjava
- Java runtime informationos
- Operating system informationseparator
- File system separatorssystem
- System properties and environment
🏗️ Server Scope Structure
The server scope is automatically populated with several unmodifiable sub-structures:
🎯 BoxLang Information (server.boxlang
)
server.boxlang
)buildDate
string
Build date of the BoxLang runtime
boxlangId
string
Unique identifier for this BoxLang instance
codename
string
Release codename for this version
cliMode
boolean
Whether running in CLI mode
debugMode
boolean
Whether debug mode is enabled
jarMode
boolean
Whether running from JAR file
modules
struct
Information about loaded modules
runtimeHome
string
Path to BoxLang runtime directory
version
string
BoxLang version number
// BoxLang runtime information
println( "BoxLang Version: #server.boxlang.version#" )
println( "Codename: #server.boxlang.codename#" )
println( "Build Date: #server.boxlang.buildDate#" )
println( "Runtime Home: #server.boxlang.runtimeHome#" )
println( "CLI Mode: #server.boxlang.cliMode#" )
println( "Debug Mode: #server.boxlang.debugMode#" )
println( "JAR Mode: #server.boxlang.jarMode#" )
// Access loaded modules
writeDump( server.boxlang.modules )
🏢 Operating System Information (server.os
)
server.os
)additionalinformation
string
Additional OS information
arch
string
System architecture (x86, x64, etc.)
archModel
string
Architecture model
hostname
string
Local machine hostname
ipAddress
string
Local machine IP address
macAddress
string
Local machine MAC address
name
string
Operating system name
version
string
Operating system version
// Operating system details
println( "OS Name: #server.os.name#" )
println( "OS Version: #server.os.version#" )
println( "Architecture: #server.os.arch#" )
println( "Hostname: #server.os.hostname#" )
println( "IP Address: #server.os.ipAddress#" )
println( "MAC Address: #server.os.macAddress#" )
☕ Java Runtime Information (server.java
)
server.java
)archModel
string
Architecture model
availableLocales
array
List of available locales
defaultLocale
string
Default system locale
executionPath
string
Java execution path
freeMemory
numeric
Available free memory in bytes
maxMemory
numeric
Maximum memory available in bytes
totalMemory
numeric
Total memory allocated in bytes
vendor
string
Java vendor name
version
string
Java version number
// Java environment details
println( "Java Version: #server.java.version#" )
println( "Java Vendor: #server.java.vendor#" )
println( "Available Memory: #server.java.freeMemory# bytes" )
println( "Max Memory: #server.java.maxMemory# bytes" )
println( "Total Memory: #server.java.totalMemory# bytes" )
println( "Default Locale: #server.java.defaultLocale#" )
// Available locales
for( locale in server.java.availableLocales ) {
println( "Locale: #locale#" )
}
📁 File System Separators (server.separator
)
server.separator
)file
string
File separator character (/ or \)
line
string
Line separator character(s)
path
string
Path separator character (; or :)
// File system separators
println( "Path Separator: #server.separator.path#" )
println( "File Separator: #server.separator.file#" )
println( "Line Separator: #server.separator.line#" )
💻 CLI Information (server.cli
)
server.cli
)args
array
Original command line arguments
command
string
Full command line used to start BoxLang
executionPath
string
Directory from which CLI was executed
parsed
struct
Parsed command line arguments
// CLI execution details (only available in CLI mode)
if( server.boxlang.cliMode ) {
println( "Execution Path: #server.cli.executionPath#" )
println( "Command: #server.cli.command#" )
writeDump( server.cli.args )
writeDump( server.cli.parsed )
}
⚙️ System Properties and Environment (server.system
)
server.system
)environment
struct
System environment variables (if security allows)
properties
struct
Java system properties (if security allows)
Security Note: The server.system
scope content depends on the populateServerSystemScope
security setting. When disabled, both environment
and properties
will be empty structs.
// System environment and properties (if security allows)
// Check security setting: populateServerSystemScope
if( !structIsEmpty( server.system.environment ) ) {
println( "Environment Variables Available" )
println( "PATH: #server.system.environment.PATH#" )
}
if( !structIsEmpty( server.system.properties ) ) {
println( "System Properties Available" )
println( "Java Home: #server.system.properties['java.home']#" )
}
🔧 Custom Server Variables
You can store your own variables in the server scope for cross-application persistence:
// Store custom server-wide data
server.appStartTime = now()
server.requestCounter = 0
server.globalSettings = {
"maintenance" : false,
"debugEnabled" : true
}
// Increment request counter
server.requestCounter++
// Check maintenance mode across applications
if( server.globalSettings.maintenance ) {
abort "System is under maintenance"
}
🔒 Unmodifiable Keys
The following keys cannot be modified once the server scope is initialized:
boxlang
- BoxLang runtime informationos
- Operating system informationjava
- Java runtime informationseparator
- File system separatorssystem
- System properties and environment
// These will throw errors after initialization:
// server.boxlang = "modified" // ❌ Cannot modify
// server.os.name = "custom" // ❌ Cannot modify
// But this is allowed:
server.customData = "allowed" // ✅ Custom keys are allowed
🌐 CGI Scope
The CGI
scope is a read-only scope available only in web runtimes that contains HTTP request information and server environment variables. It provides access to web server and request details following the Common Gateway Interface standard.
📡 Request Information
// Basic request details
println( "Request Method: #cgi.request_method#" ) // GET, POST, PUT, etc.
println( "Request URL: #cgi.request_url#" ) // Full request URL
println( "Script Name: #cgi.script_name#" ) // Request URI
println( "Query String: #cgi.query_string#" ) // URL parameters
println( "Path Info: #cgi.path_info#" ) // Additional path info
// Content information
println( "Content Type: #cgi.content_type#" ) // Request content type
println( "Content Length: #cgi.content_length#" ) // Request body size
🖥️ Server Information
// Server details
println( "Server Name: #cgi.server_name#" ) // Server hostname
println( "Server Port: #cgi.server_port#" ) // Server port
println( "Server Protocol: #cgi.server_protocol#" ) // HTTP/1.1, HTTP/2, etc.
println( "Server Port Secure: #cgi.server_port_secure#" ) // Secure port if HTTPS
// Local server information
println( "Local Address: #cgi.local_addr#" ) // Server IP address
println( "Local Host: #cgi.local_host#" ) // Server hostname
👤 Client Information
// Client/remote details
println( "Remote Address: #cgi.remote_addr#" ) // Client IP address
println( "Remote Host: #cgi.remote_host#" ) // Client hostname
println( "Remote User: #cgi.remote_user#" ) // Authenticated user
// Request security
println( "HTTPS: #cgi.https#" ) // Boolean - is secure
println( "HTTP Host: #cgi.http_host#" ) // Host header with port
📂 File Path Information
// Template and path information
println( "Template Path: #cgi.cf_template_path#" ) // Absolute template path
println( "BX Template Path: #cgi.bx_template_path#" ) // BoxLang template path
println( "Path Translated: #cgi.path_translated#" ) // Physical path
🌐 HTTP Headers
The CGI scope automatically provides access to all HTTP headers using the http_
prefix:
// Common HTTP headers
println( "User Agent: #cgi.http_user_agent#" ) // Browser/client info
println( "Accept: #cgi.http_accept#" ) // Accepted content types
println( "Accept Language: #cgi.http_accept_language#" ) // Language preferences
println( "Accept Encoding: #cgi.http_accept_encoding#" ) // Compression support
println( "Connection: #cgi.http_connection#" ) // Connection type
println( "Referer: #cgi.http_referer#" ) // Referring page
println( "Cookie: #cgi.http_cookie#" ) // Cookie header
// Custom headers are also accessible
println( "Authorization: #cgi.http_authorization#" ) // Auth header
println( "X-Forwarded-For: #cgi.http_x_forwarded_for#" ) // Proxy headers
🔍 CGI Scope Behavior
// CGI scope never throws errors - returns empty string for missing keys
println( "Non-existent key: '#cgi.nonexistent#'" ) // Returns: ""
// Check if keys exist
if( len( cgi.http_user_agent ) ) {
println( "User agent is available" )
}
// Iterate over all CGI variables
for( key in cgi ) {
println( "#key#: #cgi[key]#" )
}
// Dump all CGI information
writeDump( var=cgi, label="CGI Scope" )
🔐 Security and SSL Information
// SSL/TLS certificate information (when available)
println( "Auth Type: #cgi.auth_type#" ) // Authentication method
println( "Auth User: #cgi.auth_user#" ) // Authenticated username
println( "Cert Issuer: #cgi.cert_issuer#" ) // Certificate issuer
println( "Cert Subject: #cgi.cert_subject#" ) // Certificate subject
println( "Cert Key Size: #cgi.cert_keysize#" ) // Certificate key size
// HTTPS specific information
if( cgi.https == "on" ) {
println( "HTTPS Key Size: #cgi.https_keysize#" )
println( "HTTPS Secret Key Size: #cgi.https_secretkeysize#" )
}
📊 Available CGI Variables
The CGI scope provides access to these standard variables:
auth_password
HTTP authentication password
auth_type
HTTP authentication type
auth_user
HTTP authenticated username
bx_template_path
BoxLang template physical path
cf_template_path
CFML-compatible template path
content_length
Request content length
content_type
Request content type
context_path
Web application context path
gateway_interface
CGI version
http_*
All HTTP headers with http_
prefix
https
Whether request is secure
local_addr
Server IP address
local_host
Server hostname
path_info
Additional path information
path_translated
Physical path of template
query_string
URL query parameters
remote_addr
Client IP address
remote_host
Client hostname
remote_user
Authenticated remote user
request_method
HTTP method (GET, POST, etc.)
request_url
Complete request URL
script_name
Request URI
server_name
Server hostname
server_port
Server port number
server_port_secure
Secure server port
server_protocol
HTTP protocol version
🚫 Client Scope
The client
scope is not supported in core BoxLang. This is a CFML legacy scope that is only available via our bx-compat-cfml
module. If you would like to use it, please install the module.
# Using OS CLI
install-bx-module bx-compat-cfml
# Using CommandBox
box bx-compat-cfml
Last updated
Was this helpful?