Improve Load Times for APEX Static Files

Search for a command to run...

This is really helpful Jon - thanks. Going to get those APEX images off our ORDS server.
Introduction One of the marquee features of APEX 26.1 was AI Interactive Reports. When I started testing this new feature, I was interested to see what was going on behind the scenes. In this post, I

Introduction One of the marquee features of APEX 26.1 is APEX AI Agents. APEX AI Agents allow you to use an LLM and your own PL/SQL and JS tools to perform actions on your data instead of just chattin

Introduction In my previous post, I looked at logging APEX AI Agent requests and responses with Request and Response handlers. Logging is the first step because it shows you what is actually moving th

Introduction Logging is one of the first things you need when building serious AI Agents. Without it, debugging quickly becomes guesswork. You can see the final answer, but not always why the model ch

Context management patterns from building agents in Oracle APEX

In my experience, static files in APEX are often abused for the sake of expediency. I often see people doing the most straightforward thing rather than the right thing. This can lead to a poor user experience, unnecessary load on your ORDS server, and maintenance headaches.
Before I start, I want to level-set what static files are in relation to APEX. As the name suggests, static files cannot be changed at runtime. Static files can be referenced and loaded by APEX and the content used by your APEX applications. Examples of static files include:
For static files to be loaded by APEX, you need to specify where they are stored. APEX provides a lot of flexibility in storing and referencing static files. This post will describe the three most common places to store static files and the best practices for each. I will also discuss methods for compressing and versioning static files. Finally, I’ll share the results of the tests I ran with timings for loading static files from the three central static file locations.
This post assumes you are at least familiar with static files and have used them in your APEX applications.
The diagram below shows a typical configuration for APEX and static files.

The diagram below shows the ideal state for APEX and static files.

Static Application/Workspace Files (in the APEX application definition), a Content Delivery Network (CDN), and the ORDS File Server are the three most common places to store static files. In this section, I will describe each approach, list their benefits and drawbacks and finally describe when to use each option.
As the name suggests, Static Application Files can only be referenced within the Application in which they are stored. Static Workspace Files can be referenced from any applications within the workspace. Other than that distinction, they behave the same way, so I will only reference Static Application Files in the future.
This screenshot shows several Static Application Files, which we will be referencing throughout the post.

This is the most straightforward and obvious location to store static files. There are a few things you should consider when using Static Application Files.

#MIN# Substitution String The #MIN# substitution is empty when running your application in debug mode and is set to '.min' when not in debug mode. This is helpful when debugging issues with JS and CSS static files because it allows you to see the uncompressed file. When you are not debugging, the minified version of the file is used, and static file load times are improved.
Example Page Level JS static file reference using #MIN#

Benefits of Static Application Files
Drawbacks
When to Use
A CDN (content delivery network), also called a content distribution network, is a group of geographically distributed and interconnected servers. They provide cached internet content from a network location closest to a user to speed up its delivery. A CDN is the ultimate location for your static files. There are several CDN providers, including Cloudflare and CloudFront, from AWS.
I have used AWS CloudFront several times. CloudFront allows you to identify a source for your static files (typically an AWS S3 bucket). You then use the CloudFront provided URL to reference your static files in APEX. CloudFront takes care of syncing these files from your S3 bucket to edge servers worldwide.
You need to consider caching and file versioning when using any CDN. With CloudFront, if you change a file in the source S3 bucket, it can take up to 24 hours for that file to be pushed out to all edge servers. This means your users will be using an old file version until the cache expires (not good). CloudFront allows you to invalidate the cache and force CloudFront to fetch the latest file copy from S3. This comes with a cost if you do more than 1,000 invalidations monthly. A better option is to include version numbers in your file names (see the section on versioning below for details).
Benefits of Using a CDN
Drawbacks
When to Use
Another static file option is storing them on your ORDS file server. Note: This is only an option if you manage the ORDS server and can place files there. This includes on-premise installs or Customer Managed ORDS on Oracle Cloud Infrastructure (OCI) Autonomous database. This is not an option if you use the OCI APEX Service.
Benefits
Drawbacks
When to Use

When dealing with static files, the smaller the file, the quicker it loads. This applies no matter where you store your static files. There are several ways you can reduce the size of your static files.
From APEX 21.2 onward, APEX will minify JS and CSS for you. You can also use free online tools like JS Compress to compact the size of JavaScript and CSS Files. This can reduce file sizes significantly. In the screen shot above, the LoDash JavaScript library went from 112K down to 13K after minification.
There are desktop and on-line tools that allow you to compress jpg and png files without a significant loss in quality. It takes a few seconds to do and any reduction in file size improves static file load times. In the screen shot above, you can see the XX_Landscape.jpeg file went from 116kb to 91kb after compression.
You can get even better results by using the 'WebP' image format. WebP lossless images are 26% smaller in size compared to PNGs. WebP lossy images are 25-34% smaller than comparable JPEG images at equivalent quality.
In the screen shot above, you can see the XX_Landscape.jpeg file went from 116kb to 69kb after converting it to '.webp'.
This section describes a test case I constructed to see what difference the location and size of static files makes.
I used the following test files:
Each file was loaded into Static Application Files of an APEX Application, an AWS S3 bucket behind an AWS CloudFront CDN and an ORDS file server. The ORDS file server is an Always Free 1GB OCI Linux VM and the APEX instance is an Always Free ATP instance (both running from Ashburn in NC). The tests were run from San Diego California which is about 2,500 miles from the OCI data center. The web server is NGINX, the version of ORDS is 21.4 and the version of APEX is 21.2.
I used the Siege load testing tool to fetch each file 10 times using a single thread.
Here is an example of the Siege scripts run for the js files. Each URL is accessed 10 times (-r10) with one thread (-c1).
-- APEX Static Files
siege -c1 -r10 "https://www.example.com/ords/dev/r/cndemo/111/files/static/v13/XX_LoDash.js"
siege -c1 -r10 "https://www.example.com/ords/dev/r/cndemo/111/files/static/v13/XX_LoDash.min.js"
-- ORDS Server
siege -c1 -r10 "https://www.example.com/XX_LoDash.js"
siege -c1 -r10 "https://www.example.com/XX_LoDash.min.js"
-- CDN
siege -c1 -r10 "https://test.cloudfront.net/XX_LoDash.js"
siege -c1 -r10 "https://test.cloudfront.net/XX_LoDash.min.js"

Conclusions
I have touched on static file versioning a couple of times in this post. If you store and reference static files anywhere but APEX Static Application or Workspace files, then you need to consider file versioning. Depending on where you serve your static files from, files can be cached by a CDN and by the browser. The only way to make sure the latest version of a static file reaches your end users (other than invalidating the cache) is to somehow change the name of the file. There are two approaches for this.
The preferred approach is to use a version number in the file name e.g. XX_Landscape_v3.webp

When the browser sees that it doesn't have the file cached it will get the file from the source. Similarly with a CDN, if it sees it doesn't have a certain file name cached on its edge servers, it will fetch it from the source location and then cache it.
Benefits
Another option is to add a version number to the Query String for the file. e.g. XX_Landscape.webp?v=4

Seeing a different query string makes the browser or CDN think it has never seen the file before and it fetches the file from the source (and then caches it).
If you are in the habit of updating the version of your APEX applications (see screen shot below), then you can reference this in your static file references using the substitution string #APP_VERSION#. e.g. XX_Landscape.webp?v=#APP_VERSION#
Doing this guarantees a fresh copy of the file will be fetched after every Application Deployment.
If you think about it, the most frequently used static files (and the largest) are the ones used by APEX itself. This includes font APEX, the Universal theme, Maps, Oracle JET etc. etc. Since APEX 19.1 Oracle has provided a CDN to host the APEX product static files. See here for details.
You can configure specific applications to use the Oracle CDN by entering the CDN URL under 'Static File Prefix' in Application Shared Components:
Note: When you set the CDN URL at the Application level, then APEX will also look for Shared Components > Static Application Files under this URL. In the screen shot above, the substitution string #APP_FILES# will get changed to https://static.oracle.com/cdn/apex/21.2.2/. This means that if you want to reference custom JS, CSS or Image files, you cannot store them under Shared Components > Static Application Files. You will need to host custom static files in object storage, a file server or your own CDN and provide references to the complete URL for each file
You can set your entire instance to use the Oracle CDN by running the following from a database account with the APEX_ADMINISTRATOR_ROLE role:
begin
apex_instance_admin.set_parameter(
p_parameter => 'IMAGE_PREFIX',
p_value => 'https://static.oracle.com/cdn/apex/21.1.2/' );
commit;
end;
Note: When you set the CDN URL at the Instance level, then APEX will not look for 'Static Application Files' under this URL. This means you can add custom JS, CSS or Image files in 'Static Application Files'.
If you set the CDN at the instance level, then you do not need to host the APEX product static files on your ORDS server at all. I would suggest that unless you are restricted by outbound firewall rules then you should give your ORDS server a break and set your entire APEX instance to use the CDN. Not needing to worry about the APEX static files also makes APEX and ORDS upgrades much simpler.
CDN URL
You can get the appropriate CDN URL for your version of APEX from the 'Known Issues' page where you download APEX.

During development some solutions for storing static files can be cumbersome. For CDN and ORDS server solutions you will be constantly copying files, changing version numbers, clearing caches etc.
For Static Application Files the experience was similar unless you used something like APEX Nitro or the FOS APEX Builder Extension. APEX 21.2 improved the editing and minifying experience for Static Application Files enormously. APEX 22.1 takes this a step further by introducing Session Overrides. This essentially allows you to reference alternate static files (hosted on your laptop) during development. This allows you to change files, test and iterate with no file copying involved.
Static files are an important part of APEX. Too often expediency causes us to put static files in the easiest location thinking we will come back to it later. I challenge everyone to take some time think about how your static files will be used and choose the right approach not the quickest. Finally, you should always minify JS and CSS and use the '.webp' image format where possible.
If you use some of the approaches above, you can reduce the load on your ORDS server(s) significantly. This allows ORDS do what it does best which is to serve APEX pages and host REST web services.