Very Slow Application Load Times with Bokeh + Flask

Bokeh experts,

We are trying to use Bokeh with Flask to deliver pages with graphics through Apache2 using the bokeh.embed.components function. We are experiencing drastic slowness on the page. Digging into the logs we found that the page is sending 1/2 to 1 meg of data every time it loads, the vast majority of this data coming from the bokeh javascript files. Examining the page source we found that not only are we telling the browser to download the scripts, we are also sending the Bokeh scripts every time. Here’s a look at our page’s html:

<html lang="en" >
  <!-- Bokeh includes -->
  <script src="/static/scripts/bokeh-2.0.2.min.js" crossorigin="anonymous"></script>
  <script src="/static/scripts/bokeh-widgets-2.0.2.min.js" crossorigin="anonymous"></script>
  <script src="/static/scripts/bokeh-tables-2.0.2.min.js" crossorigin="anonymous"></script>

<script type="text/javascript">
	/* BEGIN bokeh.min.js */

<script type="text/javascript">
	/* BEGIN bokeh-widgets.min.js */

<script type="text/javascript">
	/* BEGIN bokeh-tables.min.js */

We have also tried importing bokeh.resources.INLINE to call the render_js() and render_css() functions instead of using the “bokeh…min.js” files, and there is no discernible difference between application load times. The json data used to create our plots is generally between 200-400 kb in size and isn’t appreciably contributing to the application loading times.

We must be doing something really wrong but we can’t figure out why.

  • Why isn’t the browser caching the javascript? We can’t find them cached anywhere and have used multiple browsers on multiple OS’s.
  • Why is our code sending all of them every time?

Any help will be greatly appreciated every time we load the page!


Server: Ubuntu 18.04.5 LTS

Apache: Apache/2.4.29

Python: 3.6.9

Flask: 1.1.2

Werkzeug: 1.0.1

Bokeh: 2.0.2


Brad & Bill

This literally templates the entire source text of BokehJS into every response (i.e. “inline”), so if you are looking to take advantage caching, that does the exact opposite of what you want.

Why isn’t the browser caching the javascript?

There’s not really enough complete information here to say for sure. If you are doing something that triggers the autoload handler that will initiate BokehJS loads from the Bokeh server static path by default and IIRC it generates cache-busting URLs in that case. But I’d have to know more about the actual details of what you are doing to speculate.

Regardless, I think what you want to do is use CDN resources. Those will load BokehJS from in a way that the browser can cache, and also has the benefit of removing the burden of serving BokehJS from the Bokeh server.

If you can’t use CDN resources (e.g. on on airgapped network) then you could configure Apache to serve a static dir that you copy the BokehJS files into and then configure Resources to load from that (again also has the benefit of relieving the Bokeh server of serving static files itself).

Thanks for the speedy reply. As is my understanding, the current version of the web application takes advantage of the CDN resources you mentioned. In the HTML block of code I sent, under the <!-- Bokeh includes --> comment are the scripts pulled from the output of running bokeh.resources.CDN.js_files as is instructed in this example in the “Components” section.

I’ve tried loading these js files directly from in the HTML head, as well as downloading them to our server and saving them in my /static/scripts directory. The latter is the method shown in the HTML code I’ve supplied, i.e.:
<script src="/static/scripts/bokeh-2.0.2.min.js" crossorigin="anonymous"></script>

When using the downloaded js files, we use the CacheFile directive in Apache2 and explicitly name these files in the main Apache configuration file. I believe this is what you’re getting at in your last paragraph? Neither of these two approaches caches the bokeh js files.

Perhaps I am not using the CDN resources correctly here?

In the HTML block of code I sent, under the <!-- Bokeh includes --> comment are the scripts pulled from the output of running bokeh.resources.CDN.js_files as is instructed in this example in the “Components” section.

I don’t see how that’s possible. The HTML block you sent above has URLs like


which is definitely not using CDN resources, and not the output of CDN.js_files either:

In [2]: bokeh.resources.CDN.js_files

I’ve tried loading these js files directly from in the HTML head

And what happened? You can see we set a very long cache control max-age value on the CDN responses by examining the headers yourself:

(223) ❯ curl -H "Origin:" \
  -H "Access-Control-Request-Method: GET" \
  "" -I
HTTP/2 200
content-type: application/javascript
content-length: 771252
date: Wed, 16 Dec 2020 02:07:55 GMT
access-control-allow-origin: *
access-control-allow-methods: GET, HEAD
last-modified: Mon, 19 Oct 2020 19:24:10 GMT
etag: "708688072c5bc35dc9ad32ff7573b038"
cache-control: max-age=31536000
x-amz-version-id: ajhPWgPAM6hE5INh5f7t2D.OByMKm498
accept-ranges: bytes
server: AmazonS3
x-cache: Miss from cloudfront
via: 1.1 (CloudFront)
x-amz-cf-pop: SEA19-C1
x-amz-cf-id: tFFPJ68niIIcjMMayEL9CtWJMwP6qG6KguCYGUJCNCvYtrGtAYdSvQ==

So, for example I always see these cached when running any of the examples in the repo:

That’s after a fresh browser restart. Loading the page again without restart then shows memory cache instead of disk cache.

If you can see actual loads from in your network pane but they aren’t being cached then I am afraid I have no idea what’s going on at your end.

BTW, you should not add these to your page if you are using CDN resources:

I can’t tell if you are or not from the narrative above.

Also, in case it was not evident, if you aren’t actually using and DataTable anywhere, then it is not necessary to load bokeh-widgets-table at all (likewise if you are not using any other widgets and bokeh-widgets)